Railway Operation Simulator  v2.17.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  SignallerStoppingFlag = false;
103  SignallerStopped = false;
104  SignallerRemoved = false;
105  NotInService = false;
106  HoldAtLocationInTTMode = false;
107  AllowedToPassRedSignal = false;
108  CallingOnFlag = false;
109  BeingCalledOn = false;
110  DepartureTimeSet = false;
112  TimetableFinished = false;
113  LastActionDelayFlag = false;
114  OneLengthAccelDecel = false;
115  TrainCrashedInto = -1;
117  Plotted = false;
118  TrainGone = false;
119  SPADFlag = false;
120  FrontCodePtr = new Graphics::TBitmap;
121  FrontCodePtr->PixelFormat = pf8bit;
122  FrontCodePtr->Height = 8;
123  FrontCodePtr->Width = 8;
125  FrontCodePtr->Transparent = false;
126  AValue = sqrt(2 * PowerAtRail / Mass);
128  TerminatedMessageSent = false;
129  JoinedOtherTrainFlag = false;
131  FollowOnServiceRef = ""; //added at v2.12.0
132  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
133  StepForwardFlag = false;
135  for(int x = 0; x < 4; x++)
136  {
137  HeadCodeGrPtr[x] = new Graphics::TBitmap;
138  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
139  HeadCodeGrPtr[x]->Height = 8;
140  HeadCodeGrPtr[x]->Width = 8;
142  HeadCodeGrPtr[x]->Transparent = false;
143  }
144  for(int x = 0; x < 4; x++)
145  {
146  BackgroundPtr[x] = new Graphics::TBitmap;
147  BackgroundPtr[x]->PixelFormat = pf8bit;
148  BackgroundPtr[x]->Height = 8;
149  BackgroundPtr[x]->Width = 8;
151  BackgroundPtr[x]->Transparent = false;
152  }
153  for(int x = 0; x < 4; x++)
154  {
156  // set here to ensure have values
157  }
158  for(int x = 0; x < 4; x++)
159  {
160  PlotElement[x] = -1; // marker for not plotted yet
161  }
162  for(int x = 0; x < 3; x++)
163  {
164  OldZoomOutElement[x] = -1; // marker for not plotted yet
165  }
167  NextTrainID++;
168 
169  // new values added to complete initialisation of all TTrain variables
170 
171  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
172  // TrainDataEntryPtr, initialise in AddTrain
174  FrontElementLength = 0;
175  EntrySpeed = 0;
176  ExitSpeedHalf = 0;
177  ExitSpeedFull = 0;
178  MaxExitSpeed = 0;
179  BrakeRate = 0;
181  FirstHalfMove = true;
182  EntryTime = 0;
183  ExitTimeHalf = 0;
184  ExitTimeFull = 0;
185  ReleaseTime = 0;
186  TRSTime = 0;
187  LastActionTime = 0;
188  Straddle = MidLag;
189  LeadElement = -1;
190  LeadEntryPos = 0;
191  LeadExitPos = 0;
192  MidElement = -1;
193  MidEntryPos = 0;
194  MidExitPos = 0;
195  LagElement = -1;
196  LagEntryPos = 0;
197  LagExitPos = 0;
198  TrainFailed = false; // added at v2.4.0
199  for(int x = 0; x < 4; x++)
200  {
201  HOffset[x] = 0;
202  VOffset[x] = 0;
203  PlotEntryPos[x] = 0;
204  }
205  OpTimeToAct = 60; // default value, new at v2.2.0
206  TimeToExit = -1;
207  ExitPair.first = -1;
208  ExitPair.second = -1;
209  MinsDelayed = 0.0; // new at v2.2.0
210  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
211  FinishJoinLogSent = false;
212  // added at v2.4.0 to prevent repeatdly logging the event
215  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
219  ZeroPowerNoCDTMessage = false;
224  TrainFailurePending = false;
225  SkippedDeparture = false;
226  ActionsSkippedFlag = false;
227  SkipPtrValue = 0;
228  TrainSkippedEvents = 0;
229  DelayedRandMins = 0; //added at v2.13.0
230  NewDelay = 0; //added at v2.13.0
231  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
232  ActualArrivalTime = TDateTime(0); //added at v2.13.0
233  LastSigPassedFailed = false; //added at v2.13.0
234  Utilities->CallLogPop(648);
235 }
236 
237 // ---------------------------------------------------------------------------
238 
239 void TTrain::DeleteTrain(int Caller)
240 /*
241  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
242  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
243  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
244  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
245  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
246  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
247  No need to delete HeadCodePosition as that just points to existing bitmaps
248 */{
249  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
250  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
252  if(Display->ZoomOutFlag)
253  {
255  }
256  if(FrontCodePtr == 0)
257  {
258  throw Exception("Error in attempting to delete FrontCodePtr");
259  }
260  delete FrontCodePtr;
261  FrontCodePtr = 0;
262  for(int x = 0; x < 4; x++)
263  {
264  if(BackgroundPtr[x] == 0)
265  {
266  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
267  }
268  delete BackgroundPtr[x];
269  BackgroundPtr[x] = 0;
270  }
271  for(int x = 0; x < 4; x++)
272  {
273  if(HeadCodeGrPtr[x] == 0)
274  {
275  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
276  }
277  delete HeadCodeGrPtr[x];
278  HeadCodeGrPtr[x] = 0;
279  }
280  Utilities->CallLogPop(649);
281 }
282 
283 // ---------------------------------------------------------------------------
284 
286 /*
287  Plots the train starting position on screen. Note that the check for starting on straight points &
288  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
289  ChangeDirection calls this function]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
290  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
291  Set the headcode graphics pointers from the headcode text, then check whether starting at a
292  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
293  for the continuation element. Otherwise set Lead and Mid values,
294 
295  and Lead element value unless
296  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
297  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
298  then check if a train on either Mid or Lag and if so give a warning message and return false so
299  that the calling function can delete the train. Plot the Mid element train values then do similarly
300  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
301  the train. Finally set the Plotted flag and return true.
302 */{
303  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
304  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
305 
307  // PlotStartTime = TrainController->TTClockTime;
308  FirstHalfMove = true;
309 
310  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
311  // 'claim' it for this train to prevent any other waiting trains trying to enter
313  {
314  LagElement = -1; // not to be plotted
315  LagExitPos = 0; // not to be plotted
316  LagEntryPos = 0; // not to be plotted
317  MidElement = -1; // not to be plotted
318  MidExitPos = 0; // not to be plotted
319  MidEntryPos = 0; // not to be plotted
321  LeadExitPos = 1; // will be 1 for continuation entry
322  LeadEntryPos = 0;
323 
325  MaxExitSpeed = StartSpeed; // initial value
327  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
328  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
329  if(EntrySpeed > SpeedLimit)
330  {
331  EntrySpeed = SpeedLimit;
332  }
334  {
336  }
338  // LeadElement is the element to be entered
339 
340  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
341  // can achieve ExitSpeedFull at the half braking rate.
343  {
344  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
345  if(TempEntrySpeed < EntrySpeed)
346  {
347  EntrySpeed = TempEntrySpeed;
349  }
350  }
351  Straddle = MidLag; // only for starting on a continuation
353  // no need to stop gap flashing if start on continuation
354  }
355  else // not starting at a continuation
356  {
357  LagElement = -1;
358  LagEntryPos = 0;
359  LagExitPos = 0;
366 
368  MaxExitSpeed = StartSpeed; // initial value
370  bool TempDerail = false; // dummy
371  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
373  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
374  {
375  StoppedWithoutPower = true;
376  }
377  // facing buffers check - ignore starting speed if start facing buffers
378  StoppedAtBuffers = false;
379  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
382  {
383  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
384  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
385  EntrySpeed = 0;
386  ExitSpeedHalf = 0;
387  ExitSpeedFull = 0;
388  MaxExitSpeed = 0;
389  // SetTrainMovementValues not called so set this here
390  BrakeRate = 0;
393  StoppedAtSignal = false;
394  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
395  // signal check is an 'else'
396  if(!StoppedAtLocation)
397  {
398  StoppedAtBuffers = true; // stopped at location takes precedence
399  }
400  }
401 
402  // facing continuation check - don't allow to stop even if no power
404  {
405  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
406  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
410  BrakeRate = 0;
411  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
412  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
413  }
414 
415  // Signal check
416  else if((NextElementPosition > -1) && (NextEntryPos > -1))
417  // condition check added as precaution after SloughIECC error reported by James U
418  {
419  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
420  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
421  {
422  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
423  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
424  EntrySpeed = 0;
425  ExitSpeedHalf = 0;
426  ExitSpeedFull = 0;
427  MaxExitSpeed = 0;
428  BrakeRate = 0;
431  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
432  {
433  StoppedAtSignal = true;
435  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
436  }
438  {
439  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
440  // forwards, but don't change the background colour so still shows as stopped at location
441  StoppedAtSignal = true;
442  }
443  }
444  else
445  {
446  StoppedAtSignal = false;
447  if(NextEntryPos > 1)
448  {
449  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
450  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
451  }
452  else
453  {
454  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
455  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
456  }
457  if(EntrySpeed > SpeedLimit)
458  {
459  EntrySpeed = SpeedLimit;
460  }
462  {
464  }
466  TDateTime TestTime = TrainController->TTClockTime; // test
467  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
468  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
469  // NextElement is the element to be entered
470 
471  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
472  // can achieve ExitSpeedFull at the half braking rate.
474  {
475  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
476  // half braking
477  if(TempEntrySpeed < EntrySpeed)
478  {
479  EntrySpeed = TempEntrySpeed;
480  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
481  }
482  }
483  }
484  }
486  {
487  throw Exception("Error, LeadElement Exit Connection is NotSet");
488  }
489  }
490  if(MidElement > -1) // will be -1 if start on continuation
491  {
492  Straddle = LeadMid;
496  {
497  for(int x = 0; x < 4; x++)
498  {
499  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
500  }
501  }
502  else
503  {
504  for(int x = 0; x < 4; x++)
505  {
507  }
508  }
509  if(TrainMode == Timetable)
510  {
512  }
513  else
514  {
516  }
518  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
519 
522 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
523  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
524  {
525  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
526  Utilities->CallLogPop(651);
527  return false;
528  }
529 */
534  PlotTrainGraphic(8, 0, Display);
535  PlotTrainGraphic(9, 1, Display);
536 
539 
540  // pick up background bitmaps [2] & [3]
541 
544 
545  PlotElement[2] = MidElement;
547  PlotElement[3] = MidElement;
549  PlotTrainGraphic(10, 2, Display);
550  PlotTrainGraphic(11, 3, Display);
551  // Plotted = true; set in PlotTrainGraphic
552  }
553  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
554  Utilities->CallLogPop(652);
555 }
556 
557 // ---------------------------------------------------------------------------
558 void TTrain::UnplotTrain(int Caller)
559 {
560  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
561  if(!Plotted)
562  {
563  return;
564  }
565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
566 
567  if(Straddle == MidLag)
568  {
569  if(MidElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  if(LagElement > -1)
578  {
583  // to force plot of locked route marker, needed once only for the element
584  }
585  }
586  else if(Straddle == LeadMidLag)
587  {
588  if(LeadElement > -1)
589  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(MidElement > -1)
595  {
600  // to force plot of locked route marker, needed once only for the element
601  }
602  if(LagElement > -1)
603  {
606  // to force plot of locked route marker, needed once only for the element
607  }
608  }
609  else if(Straddle == LeadMid)
610  {
611  if(LeadElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  if(MidElement > -1)
620  {
625  // to force plot of locked route marker, needed once only for the element
626  }
627  }
628  if(LeadElement > -1)
629  {
631  }
632  if(MidElement > -1)
633  {
635  }
636  if(LagElement > -1)
637  {
639  }
640  Plotted = false;
642  Display->Update();
643  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
644  // resurrected when Update() dropped from PlotOutput etc
645  Utilities->CallLogPop(653);
646 }
647 
648 // ----------------------------------------------------------------------------
649 
650 void TTrain::UpdateTrain(int Caller)
651 /*
652  Note: Some changes made since comments written
653 
654  Brief:
655  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
656  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
657  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
658  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
659  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
660  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
661  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
662  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
663  changed to MidLag within the function and all elements moved down one, old Mid becomes
664  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
665  incremented to reflect the position the train now occupies.
666 
667  Detail:
668  Set TrainFailurePending if all conditions met
669  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
670  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
671  and return.
672  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
673  If there's a LagElement (there will be but include check for good practice - next
674  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
675  train fully on offending point - Derail set and DerailPanding reset, train background
676  colour changed (note that BackgroundColour is a property of the train itself) then return.
677  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
678  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
679  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
680  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
681  if LeadElement is a fouled trailing point.
682  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
683  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
684  replotting the last background segment and checking whether the element is a bridge or crossover with the other
685  track in a route, in which case the route colour is replotted.
686  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
687  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
688  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
689  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
690  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
691  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
692  train can be deleted by the calling function, and the function returns.
693  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
694  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
695  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
696  basic red aspect.
697 
698  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
699  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
700  regardless of direction, and with the correct front code colour.
701 
702  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
703  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
704  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
705  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
706  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
707  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
708  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
709 
710  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
711  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
712  changed similarly. The function then returns.
713 
714  If Crashed is not set then Straddle is incremented and the function returns.
715 */
716 
717 {
718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
719  UpdateCounter++;
720  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
721  if(UpdateCounter >= 100)
722  {
723  UpdateCounter = 0;
724  }
725  int RandRange = (TrainController->MTBFHours * 3600) / 53;
726 
727  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
728  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
729  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
730  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
731  // RandomFailureCounter value is fixed for a full cycle of train updates so this
732  // makes sure there's no bunching of failures as there is for a fixed comparison number
733  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
734  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
735  {
736  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
737  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
738  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
739  // don't fail if:
740  // (a) on a continuation (entering or leaving);
741  // (b) already failed;
742  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
743  // (d) train terminated;
744  // (e) crashed or derailed; or
745  // (f) under signaller control and stopped.
746  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
747  {
748  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
749  // max value for RandRange is over 2x10^9
750  {
751  // here if failure due
752  TrainFailurePending = true;
753  // fail when PlotElements set to proper Lead & Mid Elements
754  }
755  }
756  }
757 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
758  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
759  {
760  StoppedWithoutPower = true;
761  }
762 */
763 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
764 // THVShortPair ExitPair; //added at v2.10.0
765  int LockedVectorNumber;
766  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
767  // default values - these needed for route checker below
768  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
769 
771  {
773  }
774  if(Crashed || Derailed)
775  {
777  {
778  PlotTrain(7, Display);
779  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
780  Display->Update();
781  }
782  OpTimeToAct = 0.0;
783  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
784  Utilities->CallLogPop(1017);
785  return; // no further action, user has to remove or work around
786  }
788  {
790  }
792  {
794  }
795  if(StoppedWithoutPower && (TrainMode == Signaller)) //added at v2.13.2 as this condition not covered - was shown as normal
796  {
798  }
800  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
801  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
802  // to move & then stop again at the same station
803  {
804  TimeTimeLocArrived = false;
805  }
806  if(!Stopped() && !SPADFlag && !TrainFailed)
807  {
809  }
810  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
811  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
812 /* old version where force a stop at buffers regardless of speed
813  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
814  else StoppedAtBuffers = false;
815 */
816 
817  // new version where crash if run into buffers
818  if(!Crashed)
819  {
820  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
821  {
822  if(ExitSpeedFull > 1)
823  {
824  Crashed = true;
828  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
829  // no need for missed action logs - will be sent when train removed
830  StoppedAtBuffers = false;
831  }
833  // stopped at location & stopped without power take precedence
834  {
835  StoppedAtBuffers = true;
836  }
837  else
838  {
839  StoppedAtBuffers = false;
840  }
841  }
842  else
843  {
844  StoppedAtBuffers = false;
845  }
846  }
847  else
848  {
849  StoppedAtBuffers = false;
850  }
851  // if crashed don't want stopped at buffers set
852 
853  // also crash if run into a level crossing that is changing or has barriers up
854  if(!Crashed)
855  {
856  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
857  {
858  int H = Track->TrackElementAt(873, LeadElement).HLoc;
859  int V = Track->TrackElementAt(874, LeadElement).VLoc;
860  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
861  {
862  Crashed = true;
866  // no need for missed action logs - will be sent when train removed
867  }
868  }
869  }
871  {
873  }
874  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
876  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
877  {
878  HoldAtLocationInTTMode = true;
879  }
880  else if(TrainMode == Timetable)
881  {
882  HoldAtLocationInTTMode = false;
883  }
884  // in Signaller mode HoldAtLocationInTTMode not changed
885 
886  // check if departure pending & set times unless already set
887  if(TrainMode == Timetable)
888  {
890  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
891  {
892  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
893  {
894  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
895  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
896  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
897  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
898  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
899  "of a shortage of on train crew"};
900  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
901  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
902  //at start or have no departure time set.
903  NewDelay = 0; //section relating to random delays added at v2.13.0
904  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
905  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
906  if(DwellTime < TDateTime(30.0 / 86400))
907  {
908  DwellTime = TDateTime(30.0 / 86400);
909  }
910  int randval = random(10000);
911  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
912  {
913  if(Utilities->DelayMode == Minor)
914  {
915  if(randval < Utilities->MinorDelayCutoff)
916  {
917  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
918  }
919  }
920  else if(Utilities->DelayMode == Moderate)
921  {
922  if(randval < Utilities->ModerateDelayCutoff)
923  {
925  }
926  }
927  else if(Utilities->DelayMode == Major)
928  {
929  if(randval < Utilities->MajorDelayCutoff)
930  {
932  }
933  }
934  }
935 //NewDelay = 25; //test
936  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
937  { //then don't delay. Added at v2.13.0
938  NewDelay = 0;
939  }
940  if(NewDelay < 1)
941  {
942  NewDelay = 0;
943  }
944  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
945  {
946  NewDelay = 0;
947  }
948  else
949  {
950  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
951  }
952  if(DelayedRandMins > 0)
953  {
954  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
955  }
956  if(DelayedRandMins < 0)
957  {
958  DelayedRandMins = 0;//can't be less than zero
959  }
961  {
962  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
963  //the formula above already includes knock-on effects
964  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
965 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
966  }
967  else
968  {
969  NewDelay = 0;
970 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
971  }
972  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
973  if(NewDelay < 0.5) //less than the 30 secs min interval
974  {
975  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
976  }
977  if(ReleaseTime < TimetableReleaseTime)
978  {
979  ReleaseTime = TimetableReleaseTime; //back to correct time
980  NewDelay = 0;
981  DelayedRandMins = 0;
982  }
983  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
984  {
985  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
986  }
987 
988  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
989  {
991  }
992  //may be possible to simplify all the above but as it seems to work ok leave as is
993  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
994  {
996  if(int(NewDelay) == 1)
997  {
999  ActionVectorEntryPtr->LocationName + " by 1 minute");
1000  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
1001  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1002  TrainController->StopTTClockMessage(140, HeadCode + " delayed at " +
1003  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1004  }
1005  else
1006  {
1007  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1008  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1009  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1010  {
1011  int randval2 = rand() % 24; //24 reasons
1012  AnsiString Reason = ReasonArray[randval2];
1014  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1015  " minutes because " + Reason);
1016  TrainController->StopTTClockMessage(141, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1017  " minutes because " + Reason);
1018  }
1019  else
1020  {
1022  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1023  " minutes because of a minor problem");
1024  TrainController->StopTTClockMessage(142, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1025  " minutes because of a minor problem");
1026  }
1027  }
1028  }
1029  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1030  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1031  DepartureTimeSet = true;
1032  }
1033  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1034  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1035  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1036  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1037  NewDelay = 0;
1038  DelayedRandMins = 0;
1040  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1041  {
1042  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1043  }
1044  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1045  DepartureTimeSet = true;
1046  }
1047  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1048  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1049  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1050  //from mixing modifications so best avoided.
1051  NewDelay = 0;
1052  DelayedRandMins = 0;
1054  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1055  {
1056  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1057  }
1058  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1059  DepartureTimeSet = true;
1060  }
1061  }
1062  }
1063  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1064  {
1065  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1066 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1067 // this->ExitPair = ExitPair;
1068  // calculate every 1 sec (in real time, not timetable time) for all trains
1069  }
1070  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1071  if(TrainMode == Timetable)
1072  {
1073  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1074  {
1075  RemainHereLogNotSent = true;
1076  }
1078  {
1079  // ignore TimeLoc & TTLoc departures
1080  // Action logs given in functions
1082  LastActionTime + TDateTime(30.0 / 86400)))
1083  {
1084  if(ActionVectorEntryPtr->Command == "fsp")
1085  {
1086  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1087  FrontTrainSplit(0);
1088  if(TrainFailurePending) // ok, stopped so PlotElements set
1089  {
1090  TrainHasFailed(0);
1091  }
1092  Utilities->CallLogPop(2041);
1093  return;
1094  }
1095  else if(ActionVectorEntryPtr->Command == "rsp")
1096  {
1097  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1098  RearTrainSplit(0);
1099  if(TrainFailurePending) // ok, stopped so PlotElements set
1100  {
1101  TrainHasFailed(1);
1102  }
1103  Utilities->CallLogPop(2042);
1104  return;
1105  }
1106  else if(ActionVectorEntryPtr->Command == "Fjo")
1107  {
1108  FinishJoin(0);
1109  }
1110  else if(ActionVectorEntryPtr->Command == "jbo")
1111  {
1112  JoinedBy(0);
1113  }
1114  else if(ActionVectorEntryPtr->Command == "cdt")
1115  {
1116  ChangeTrainDirection(0, false);
1117  }
1118  else if(ActionVectorEntryPtr->Command == "dsc")
1119  {
1120  Description = ActionVectorEntryPtr->NewDescription; //changed at v2.16.1
1124  }
1125  else if(ActionVectorEntryPtr->Command == "Fns")
1126  {
1127  NewTrainService(0, false);
1128  }
1129  else if(ActionVectorEntryPtr->Command == "Frh")
1130  {
1131  RemainHere(0);
1132  }
1133  else if(ActionVectorEntryPtr->Command == "Fer")
1134  {
1135  TimetableFinished = true;
1136  }
1137  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1138  else if(ActionVectorEntryPtr->Command == "F-nshs")
1139  {
1141  }
1142  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1143  {
1144  RepeatShuttleOrRemainHere(0, false);
1145  }
1146  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1147  {
1149  }
1150 /*
1151  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1152  shuttle headcode (no train creation)
1153  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1154  remain here
1155  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1156  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1157 */
1158  }
1159  }
1160  else
1161  {
1163  {
1165  }
1166  }
1167  }
1168  if(TrainMode == Timetable)
1169  {
1170  if(StoppedAtBuffers)
1171  {
1172  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1173  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1174  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1175  if(BufferLocation == "")
1176  {
1178  }
1179  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1180  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1181  {
1185  {
1187  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1188  // Drop missed actions so user can still use sig mode to get back on track
1190  }
1191  if(TrainFailurePending) // ok, stopped so PlotElements set
1192  {
1194  TrainHasFailed(2);
1195  }
1196  Utilities->CallLogPop(1020);
1197  return;
1198  }
1199  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1200  ReleaseTime))
1201  {
1204  {
1207  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1208  // Drop missed actions so user can still use sig mode to get back on track
1210  }
1211  if(TrainFailurePending) // ok, stopped so PlotElements set
1212  {
1214  TrainHasFailed(3);
1215  }
1216  Utilities->CallLogPop(1397);
1217  return;
1218  }
1219  }
1220  else
1221  {
1223  }
1224  }
1225  else
1226  {
1228  }
1229  if(TrainMode == Timetable)
1230  {
1232  {
1234  }
1236  {
1238  }
1239  }
1240  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1241  // restart after stopped for train in front
1242  int NextElementPosition, NextEntryPos;
1243 
1244  if(LeadElement > -1) // if an exit continuation then not set
1245  {
1246  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1247  {
1249  }
1250  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1251  {
1252  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1253  {
1254  LeadExitPos = 1;
1255  }
1256  else
1257  {
1258  LeadExitPos = 3;
1259  }
1260  }
1261  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1262  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1263  }
1264  else
1265  {
1266  NextElementPosition = -1;
1267  NextEntryPos = -1;
1268  }
1269  if((NextElementPosition > -1) && (NextEntryPos > -1))
1270  // may be buffers or continuation so need this check
1271  {
1272 /*
1273  Check whether calling-on conditions met:-
1274  a) approaching train has stopped at a signal but not at a location;
1275  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1276  change of direction (cdt), remaining here (Frh), or under signaller control);
1277  c) at least 1 platform available for the approaching train;
1278  d) points (if any) set for direct route into platform;
1279  e) approaching train is to stop at station;
1280  f) no more facing signals between train and platform;
1281  g) [dropped g]
1282  h) train in front preventing route being set far enough to release stop signal;
1283  i) train in front not exiting at continuation;
1284  j) signal must be within 4km of the stop platform;
1285  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1286  l) no existing route conflicts with the route into the platform; and
1287  m) not failed or without power (these added at v2.10.0)
1288  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1289  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1290 */
1291  if(TrainMode == Timetable)
1292  {
1293  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1294  {
1295  CallingOnFlag = true;
1296  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1297  }
1298  else
1299  {
1300  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1301  {
1302  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1303  {
1305  }
1306  }
1307  CallingOnFlag = false;
1308  }
1309  }
1310  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1311  {
1312  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1313  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1314  // sets StoppedAtSignal again & train doesn't move
1315  StoppedAtSignal = false;
1316  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1317  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1318  // LeadMidLag and front of train was on LeadElement (after the current move)
1320  EntrySpeed = 0;
1322  FirstHalfMove = true;
1323  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1324  // NextElement is the element to be entered
1325  }
1327  {
1328  if(ClearToNextSignal(0))
1329  {
1330  StoppedForTrainInFront = false;
1331  BeingCalledOn = false;
1332  EntrySpeed = 0;
1334  FirstHalfMove = true;
1335  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1336  }
1337  else
1338  {
1339  if(TrainFailurePending) // ok, stopped so PlotElements set
1340  {
1341  TrainHasFailed(4);
1342  }
1343  Utilities->CallLogPop(1097);
1344  return;
1345  }
1346  }
1347  }
1348  if((Straddle == MidLag) && (LeadElement != -1))
1349  // later check only for Straddle == LeadMid, so need this check here for initial train start
1350  {
1352  }
1353 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1354  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1355  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1356  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1357  which could be when start as Snt.
1358  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1359  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1360  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1361  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1362  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1363  reached.
1364  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1365  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1366  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1367  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1368  sending a message to the performancelog.
1369 */
1370 
1371  if(TrainMode == Timetable)
1372  {
1374  {
1375  if(BeingCalledOn)
1376  {
1377  StoppedForTrainInFront = true;
1378  }
1380  {
1382  }
1383  else //added at v2.14.0 as if a train ready to depart (pink b'gnd) taken under sig control then restored to tt control b'gnd stayed pink,
1384  { //even though release time now 30 seconds after tt control restored
1386  }
1388  {
1389  // value updated at every scheduled departure & arrival
1391  AnsiString StationName;
1393  {
1395  }
1397  {
1399  }
1400  else
1401  {
1402  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1403  }
1404  EntrySpeed = 0;
1408  FirstHalfMove = true;
1409  StoppedAtLocation = false;
1410 
1411  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1412  {
1413  StoppedWithoutPower = true;
1414  }
1415  if((NextElementPosition > -1) && (NextEntryPos > -1))
1416  // condition check added for SloughIECC error reported by James U
1417  {
1418  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1419  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1420  {
1421  StoppedAtSignal = true;
1422  if(!StoppedWithoutPower)
1423  // if stopped without power just keep existing background colour
1424  {
1426  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1427  }
1428  }
1429  }
1431  {
1432  TimeTimeLocArrived = false;
1433  LogAction(27, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->DepartureTime, false);
1434  // no warning for TimeTimeLoc departure
1435  }
1436  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1437  {
1438  LogAction(36, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1439  }
1440  else //must be TimeLoc departure
1441  {
1443  }
1444  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1445  DepartureTimeSet = false;
1446  // no need to set LastActionTime for a departure
1447  //deal here with departure pointer change, increment if SkippedDeparture
1448  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1449 
1450  if(SkippedDeparture)
1451  {
1454  TrainSkippedEvents = 0;
1455  SkippedDeparture = false;
1456  SkipPtrValue = 0;
1457  ActionsSkippedFlag = false;
1458  }
1459  else
1460  {
1462  }
1463  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1464  // note
1465 /*
1466  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1467  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1468  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1469  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1470  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1471  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1472  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1473 */
1475  {
1476  StoppedAtBuffers = true;
1477  }
1478  else if(!StoppedWithoutPower)
1479  // if buffers or no power, don't set values
1480  {
1482  {
1483  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1484  // NextElement is the element to be entered
1485  }
1486  else
1487  {
1489  // use LeadElement for an exit continuation
1490  }
1491  }
1492  }
1493  }
1494  }
1495  if(Straddle == LeadMidLag) //train on a half element
1496  {
1498  {
1499  Utilities->CallLogPop(654);
1500  return;
1501  }
1502  }
1503  else //train fully on 2 elements
1504  {
1506  {
1507  Utilities->CallLogPop(655);
1508  return;
1509  }
1510  }
1511  if((LeadElement > -1) && (MidElement > -1))
1512  {
1514  {
1515  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1516  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1517  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1518  SignallerStoppingFlag = false;
1519  StepForwardFlag = false;
1520  }
1521  }
1522  if(Stopped())
1523  // this is what prevents another movement if the train is stopped
1524  {
1525  if(TrainFailurePending) // ok, stopped so PlotElements set
1526  {
1527  TrainHasFailed(5);
1528  }
1529  BrakeRate = 0;
1530  Utilities->CallLogPop(656);
1531  return;
1532  }
1533 
1534  // HERE WHEN READY FOR NEXT MOVE
1535 
1536  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1537  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1538  if(Straddle == LeadMid) //fully on 2 elements
1539  {
1540  if(LeadElement > -1)
1541  {
1542  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1543  {
1545  if(TIF.TrackType == SignalPost)
1546  {
1547  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1548  int TIFExitPos = 0;
1549  if(TIFEntryPos == 0)
1550  {
1551  TIFExitPos = 1;
1552  }
1553  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal && !TIF.CallingOnSet) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1555  {
1556  SPADFlag = true; // user has to intervene to reset & restart after spad
1557  }
1558  }
1559  }
1560  }
1561  }
1562 
1563  // check for train in front & if so stop at next access (when train fully on element next to train)
1564  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1565  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1566  // variable TrainInFrontInSignallerModeFlag
1567  {
1568  if(LeadElement > -1)
1569  {
1570  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1571  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1572  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1573  // true if another train on NextEntryPos track whether bridge or not
1574  {
1575  StoppedForTrainInFront = true;
1576  }
1577  else
1578  {
1579  StoppedForTrainInFront = false;
1580  }
1581  }
1582  }
1583  if((Straddle == LeadMid) && SPADFlag)
1584  // give message + plot background when ready to move half past the signal
1585  {
1586  if(NextElementPosition > -1)
1587  {
1588  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1589  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1590  {
1591  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1593  // if goes past 2 signals then give message twice
1595  }
1596  }
1597  }
1598  if(Straddle == LeadMidLag)
1599  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1600  {
1601  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1602  if(SPADFlag)
1603  {
1604  if(ExitSpeedFull == 0)
1605  {
1606  StoppedAfterSPAD = true;
1607  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1608  }
1609  }
1611  {
1612  if(ExitSpeedFull == 0)
1613  {
1614  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1615  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1616  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1617  // is sent at the right time and once only.
1618  SignallerStopped = true;
1619  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1620  StepForwardFlag = false;
1621  SignallerStoppingFlag = false;
1622  TTrackElement TE;
1623  AnsiString Loc = "";
1624  bool LocNamed = false;
1625  if(LeadElement > -1)
1626  {
1627  TE = Track->TrackElementAt(782, LeadElement);
1628  if(TE.ActiveTrackElementName != "")
1629  {
1630  Loc = TE.ActiveTrackElementName;
1631  LocNamed = true;
1632  }
1633  else
1634  {
1635  Loc = "track element " + TE.ElementID;
1636  }
1637  }
1638  if((MidElement > -1) && !LocNamed)
1639  {
1640  TE = Track->TrackElementAt(783, MidElement);
1641  if(TE.ActiveTrackElementName != "")
1642  {
1643  Loc = TE.ActiveTrackElementName;
1644  LocNamed = true;
1645  }
1646  else if(Loc == "")
1647  {
1648  Loc = "track element " + TE.ElementID;
1649  }
1650  }
1651  if(Loc == "")
1652  {
1653  Loc = "outside railway";
1654  // must have stopped after left at a continuation (because both lead & mid == -1)
1655  }
1656  else
1657  {
1658  Loc = "at " + Loc;
1659  }
1660  LogAction(30, HeadCode, "", SignallerStop, Loc, "", TrainController->TTClockTime, false); // false for warning
1661  }
1662  }
1663  if(LeadElement > -1) // if an exit continuation then not set
1664  {
1665  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1666  {
1668  }
1669  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1670  {
1671  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1672  {
1673  LeadExitPos = 1;
1674  }
1675  else
1676  {
1677  LeadExitPos = 3;
1678  }
1679  }
1680  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1681  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1682  }
1683  else
1684  {
1685  NextElementPosition = -1;
1686  NextEntryPos = -1;
1687  }
1690  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1691 
1692  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1693  {
1694  StoppedWithoutPower = true;
1695  }
1696  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1697  // may be buffers or continuation. SPADFlag added at v2.1.0
1698  // so don't override the SPAD colour & don't set StoppedAtSignal
1699  {
1700  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1701  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1702  {
1703  StoppedAtSignal = true;
1704  if(!StoppedWithoutPower)
1705  // leave background as is if no power, but set StoppedAtSignal
1706  {
1708  }
1709  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1710  }
1711  }
1712  if(!Stopped())
1713  {
1714  if((NextElementPosition > -1) && (NextEntryPos > -1))
1715  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1716  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1717  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1718  // function for fuller explanation
1719  {
1720  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1721  // NextElement is the element to be entered
1722  }
1723  // follow the continuation exits:-
1724  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1725  {
1727  // Use LeadElement for calcs if lead is a continuation
1728  }
1729  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1730  {
1732  // Use MidElement for calcs if mid is a continuation
1733  }
1734  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1735  {
1737  // Use LagElement for calcs if lag is a continuation
1738  }
1739  }
1740  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1741  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1742  // Trains may not be in a route
1743  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1744  {
1745  // NB if LeadElement == -1 then the above test returns NoRoute
1746  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1747  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1748  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1749  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1750  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1751  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1752  FirstPair.second).GetELink() == TempELink))
1753  {
1754  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1755  }
1756  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1757  SecondPair.second).GetELink() == TempELink))
1758  {
1759  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1760  }
1761  }
1762  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1763  // Trains may not be in a route
1764  {
1765  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1766  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1767  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1768  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1769  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1770  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1771  FirstPair.second).GetELink() == TempELink))
1772  {
1773  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1774  }
1775  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1776  SecondPair.second).GetELink() == TempELink))
1777  {
1778  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1779  }
1780  }
1781  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1782  // Trains may not be in a route
1783  {
1784  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1785  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1786  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1787  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1788  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1789  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1790  FirstPair.second).GetELink() == TempELink))
1791  {
1792  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1793  }
1794  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1795  SecondPair.second).GetELink() == TempELink))
1796  {
1797  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1798  }
1799  AllRoutes->CheckMapAndRoutes(8); // test
1800  }
1801  if(LagElement > -1)
1802  // not entering at a continuation so can deal with train leaving the lag element
1803  {
1805  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1806  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1807  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1808 
1809  TPrefDirElement PrefDirElement;
1810  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1811  // as this is a 16x16 graphic
1813  {
1815  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1816  }
1817  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1818  {
1819  int RouteNumber;
1820  TrainGone = true;
1821  // flag to indicate train to be deleted - outside this function
1823  {
1824  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1825  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1826  // calc distance from & inc last signal to exit
1827  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1828  int NewLastElement = 0, NewLastExitPos = 0;
1829  // need above because can't change LastElement & LastExitPos until both new values obtained
1830  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1831  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1832  LastElement).TrackType != Points))
1833  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1834  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1835  // leave CumDistance as it was in these circumstances.
1836  {
1837  if(LastExitPos < 2)
1838  {
1839  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1840  }
1841  else
1842  {
1843  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1844  }
1845  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1846  if(NewLastElement == -1)
1847  // this will catch buffers or any other connection failure
1848  {
1849  break; //throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of
1850  } //10/02/23, had two continuations linked with no signal between
1851  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)]; //so when train exited this routine tracked
1852  if(NewLastExitPos == -1) //back to the entry continuation which had no further connection - doesn't need to be an error at all!
1853  {
1854  break; //throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of 10/02/23 , see above
1855  }
1856  LastElement = NewLastElement;
1857  LastExitPos = NewLastExitPos;
1858  }
1859  // if at signal add this in too (may not be signal if 'break;' encountered but doesn't matter)
1860  if(CumDistance < 1200)
1861  {
1862  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1863  }
1864  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1865  // else use 1200m - CumDistance
1866  int FirstDistance = 0;
1867  if(CumDistance >= 1200)
1868  {
1869  FirstDistance = 100;
1870  }
1871  else
1872  {
1873  FirstDistance = 1200 - CumDistance;
1874  }
1875  if(FirstDistance < 100)
1876  {
1877  FirstDistance = 100; // don't allow < 100
1878  }
1879  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1880  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1881  if(ExitSpeedFull > 20.0)
1882  {
1883  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1884  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1885  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1886  // 4320.0 = 3.6 * 1200, .0 to make it a double
1887  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1888  }
1889  else
1890  {
1891  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs between each action
1892  ContinuationAutoSigEntry.SecondDelay = 120.0;
1893  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1894  }
1895  ContinuationAutoSigEntry.AccessNumber = 0;
1896  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1898  {
1900  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1901  VectorIT++)
1902  {
1903  if(VectorIT->RouteNumber == RouteNumber)
1904  {
1905  // another train has passed out of same route so erase earlier entry
1906  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1907  break;
1908  }
1909  }
1910  }
1911  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1912  }
1914  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1915  Display->Update();
1916  // need to keep this since Update() not called for PlotSmallOutput as too slow
1917  Utilities->CallLogPop(659);
1918  return;
1919  }
1920  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1921  if(LeadElement > -1)
1922  {
1923  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1924  if(TE.Config[LeadExitPos] == Signal)
1925  // changed to lead so reset early
1926  {
1927  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1928  TE.Attribute = 0; // red
1929  int RouteNumber; //only used for autosigs routes
1930  //add chance to fail when train passes a signal
1931  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->FailureMode != FNil) &&
1932  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice, calling on signal can't fail
1933  {
1935  IFE.TVPos = LeadElement;
1936  TE.Failed = true;
1937  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1938  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1939  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1940  " failed when changing aspect.\nTrains can only pass under signaller control.");
1941  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1942  LastSigPassedFailed = true;
1943  //set repair time, random value in minutes between 10 and 179
1944  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1945  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1946  IFE.RepairTime = RepairTime;
1948  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1949  }
1950  TE.CallingOnSet = false;
1951  // don't plot if zoomed out
1952  if(!Display->ZoomOutFlag)
1953  {
1955  }
1956  // covers signal resetting in same direction
1957  }
1958  }
1960  {
1961  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1962  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1963  {
1964  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1965  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1966  TPrefDirElement PrefDirElement;
1967  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1969  {
1971  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1972  }
1974  LockedVectorNumber)))
1975  {
1977  }
1978  }
1979  }
1980  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1981  {
1982  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1984  // don't plot if zoomed out
1985  if(!Display->ZoomOutFlag)
1986  {
1988  }
1989  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1990  }
1992  {
1993  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1994  {
1995  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1996  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1997  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1998  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1999  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
2000  int RouteNumber;
2002  // already know it's an autosigsroute, this is just to get the RouteNumber
2003  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
2004  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
2005  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
2006  int RouteNumber2;
2008  // already know it's an autosigsroute, this is just to get the RouteNumber
2009  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
2010  // note that if not in a route (as likely) then RouteNumber2 set to -1
2011  {
2012  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
2013  // this was in the 1.3.0 addition but without the condition
2014  }
2015  // end of 1.3.2 addition
2016  // end of 1.3.0.addition
2017  }
2018  TPrefDirElement PrefDirElement;
2019  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
2021  {
2023  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2024  }
2025  }
2026  }
2027  }
2028  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2029  if(Straddle == LeadMid)
2030  {
2031  AllowedToPassRedSignal = false;
2032  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2033  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2034  if(DerailPending)
2035  // set during last GetLeadElement, but only act on it when train fully on offending point
2036  // i.e. next time Straddle reaches LeadMid
2037  {
2038  Derailed = true;
2039  DerailPending = false;
2043  Utilities->CallLogPop(657);
2044  return;
2045  }
2052  Straddle = MidLag;
2053  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2054  // LeadElement during this function (note that if stopped at signal then won't get this far)
2055  if(LeadElement > -1)
2056  {
2058  // i.e an exit continuation only
2059  // if don't exclude entry continuations then can't progress past it
2060  {
2061  LeadElement = -1;
2062  }
2063  else
2064  {
2065  GetLeadElement(0);
2066  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2068  if(Stopped())
2069  {
2070  if(TrainFailurePending) // ok, stopped so PlotElements set
2071  {
2072  TrainHasFailed(6);
2073  }
2074  Utilities->CallLogPop(658);
2075  return; // i.e. don't move forward one step if next element is a red signal
2076  }
2077  }
2078  }
2079  }
2080  if(LagElement > -1)
2081  {
2082  // below are the actions required at both half moves for LagElement > -1
2084 
2085  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2086  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2087  // need to do this for each half element
2088 
2089  TPrefDirElement PrefDirElement;
2090  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2091  {
2092  int RouteNumber; // holder for call below - not used
2094  {
2095  if(Utilities->clTransparent == TColor(0xFFFFFF))
2096  // change to black for a white background
2097  {
2099  // only applies for AutoSigs Route in case was locked & timed out
2100  }
2101  else
2102  // change to white for a dark background
2103  {
2105  // only applies for AutoSigs Route in case was locked & timed out
2106  }
2108  }
2109  }
2111  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2112  // or a train on the opposite track - needs to be replotted
2113  }
2114  // update all array values
2115  HOffset[3] = HOffset[2];
2116  HOffset[2] = HOffset[1];
2117  HOffset[1] = HOffset[0];
2118  VOffset[3] = VOffset[2];
2119  VOffset[2] = VOffset[1];
2120  VOffset[1] = VOffset[0];
2121  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2122 
2123  BackgroundPtr[3] = BackgroundPtr[2];
2124  BackgroundPtr[2] = BackgroundPtr[1];
2125  BackgroundPtr[1] = BackgroundPtr[0];
2126  BackgroundPtr[0] = TempPtr;
2127 
2128  // update headcode graphics depending on Lead entry value
2129  if(LeadElement > -1) // if Lead is -1 then stays as is
2130  {
2132  {
2133  for(int x = 0; x < 4; x++)
2134  {
2135  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2136  }
2137  }
2138  else
2139  {
2140  for(int x = 0; x < 4; x++)
2141  {
2143  }
2144  }
2145  }
2146  if(TrainMode == Timetable)
2147  {
2149  }
2150  else
2151  {
2153  }
2155 
2156  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2157  if(LeadElement > -1)
2158  {
2159  if(Straddle == MidLag)
2160  // just about to move half onto the new lead element
2161  {
2163  // pick up new background bitmap [0]
2165  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2166  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2167  // check if own ID for entry at continuation, else crashes into itself!
2168  {
2169  // OK if crossing on a bridge
2170  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2171  if(OtherTrainEntryPos == -1)
2172  {
2173  throw Exception("Error - OtherTrainEntryPos not set");
2174  }
2175  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2176  // LeadEntryPos for rear end crashes
2177  (LeadExitPos == OtherTrainEntryPos))
2178  // LeadExitPos for head-on crashes
2179  {
2181  Crashed = true; // only set if Straddle = MidLag
2182  CallingOnFlag = false;
2183  // in case was set, need to disable call on if call on button had been pressed
2184  }
2185  }
2186  else if(MidElement > -1) // will be -1 for continuation entries
2187  {
2188  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2189  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2190  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2191  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2192  int OtherTrainID = -1;
2193  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2194  {
2195  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2196  {
2197  TrainCrashedInto = OtherTrainID;
2198  Crashed = true; // only set if Straddle = MidLag
2199  CallingOnFlag = false;
2200  // in case was set, need to disable call on if call on button had been pressed
2201  }
2202  }
2203  }
2204  }
2205  else
2206  {
2208  // pick up new background bitmap [0]
2210  }
2211  PlotElement[0] = LeadElement;
2213  PlotTrainGraphic(12, 0, Display);
2214  }
2215  if(MidElement > -1)
2216  {
2217  PlotElement[2] = MidElement;
2219  PlotTrainGraphic(1, 2, Display);
2220  }
2221  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2222  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2223  if(Straddle == MidLag)
2224  {
2225  if(MidElement > -1)
2226  {
2227  PlotElement[1] = MidElement;
2229  PlotTrainGraphic(2, 1, Display);
2230  }
2231  if(LagElement > -1)
2232  {
2233  PlotElement[3] = LagElement;
2235  PlotTrainGraphic(3, 3, Display);
2236  }
2237  }
2238  else // Straddle == LeadMidLag
2239  {
2240  if(LeadElement > -1)
2241  {
2242  PlotElement[1] = LeadElement;
2244  PlotTrainGraphic(4, 1, Display);
2245  }
2246  if(MidElement > -1)
2247  {
2248  PlotElement[3] = MidElement;
2250  PlotTrainGraphic(5, 3, Display);
2251  }
2252  }
2253  if(Crashed)
2254  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2255  {
2260  // in case was set, need to disable call on if call on button had been pressed
2267  Straddle = LeadMidLag;
2268  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2269  Display->Update();
2270  // resurrected when Update() dropped from PlotOutput etc
2271  Utilities->CallLogPop(660);
2272  return;
2273  }
2274  // deal here with station stops & pass times after all replotting done but before Straddle changed
2275  if(TrainMode == Timetable)
2276  {
2277  if(Straddle == LeadMidLag)
2278  {
2279  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2280  {
2281  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2282  // to point to the location arrival entry - before a change of direction
2283  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2284  bool StopRequired = false;
2285  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2286  if(TTVPos > -1) // -1 if can't find it or if name is ""
2287  {
2288  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2289  // or a station where next element contains a train or a stop signal, if so
2290  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2291  // to test the actual track the train is on since it can't be a platform
2292  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2293  TTrackElement NextTrackElement; // default for now
2294  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2295  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2296  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2297  int NextElementEntryPos = -1;
2298  int NextElementExitPos = -1;
2299  bool TrainOnNextElement = false;
2300  bool StopSignalAtNextElement = false;
2301  if(ForwardConnection)
2302  // if no forward connection can't derive anything from it without errors
2303  {
2304  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2305  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2306  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2307  // this is only for signals so no need to worry about points ambiguity
2308  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2309  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2310  }
2311  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2312  {
2313  if(TTVPos > 0)
2314  {
2316  ActionVectorEntryPtr += TTVPos;
2317  }
2318  if(StopRequired)
2319  {
2320  StoppedAtLocation = true;
2321  StoppedAtSignal = false;
2322  // may have been set earlier at line 925 so need to reset as
2323  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2324  // in zoom out mode
2325  if(!TrainFailed)
2326  {
2328  // pale green
2329  }
2331  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2333  {
2334  TimeTimeLocArrived = true;
2335  // used in case of later signaller control, when need to know
2336  // whether had arrived or not, to avoid sending the arrival
2337  // message twice, see TInterface::TimetableControl1Click
2338  }
2339  }
2340  else
2341  {
2343  }
2345  {
2347  }
2348  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2350  }
2351  }
2352  }
2353  }
2354  }
2355  if(Straddle == MidLag)
2356  {
2357  Straddle = LeadMidLag;
2358  FirstHalfMove = false;
2359  }
2360  else if(Straddle == LeadMidLag)
2361  {
2362  Straddle = LeadMid;
2363  FirstHalfMove = true;
2364  }
2365  else if(Straddle == LeadMid)
2366  {
2367  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2368  }
2369  if(TrainFailurePending) // ok, moving but PlotElements set above
2370  {
2371  TrainHasFailed(7);
2372  }
2373  Display->Update();
2374  // need to keep this since Update() not called for PlotSmallOutput as too slow
2375  Utilities->CallLogPop(661);
2376 }
2377 
2378 // ----------------------------------------------------------------------------
2379 
2380 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2381 {
2382  switch(CodeChar)
2383  {
2384  case '0':
2385  return(RailGraphics->Code0);
2386 
2387  case '1':
2388  return(RailGraphics->Code1);
2389 
2390  case '2':
2391  return(RailGraphics->Code2);
2392 
2393  case '3':
2394  return(RailGraphics->Code3);
2395 
2396  case '4':
2397  return(RailGraphics->Code4);
2398 
2399  case '5':
2400  return(RailGraphics->Code5);
2401 
2402  case '6':
2403  return(RailGraphics->Code6);
2404 
2405  case '7':
2406  return(RailGraphics->Code7);
2407 
2408  case '8':
2409  return(RailGraphics->Code8);
2410 
2411  case '9':
2412  return(RailGraphics->Code9);
2413 
2414  case 'A':
2415  return(RailGraphics->CodeA);
2416 
2417  case 'B':
2418  return(RailGraphics->CodeB);
2419 
2420  case 'C':
2421  return(RailGraphics->CodeC);
2422 
2423  case 'D':
2424  return(RailGraphics->CodeD);
2425 
2426  case 'E':
2427  return(RailGraphics->CodeE);
2428 
2429  case 'F':
2430  return(RailGraphics->CodeF);
2431 
2432  case 'G':
2433  return(RailGraphics->CodeG);
2434 
2435  case 'H':
2436  return(RailGraphics->CodeH);
2437 
2438  case 'I':
2439  return(RailGraphics->CodeI);
2440 
2441  case 'J':
2442  return(RailGraphics->CodeJ);
2443 
2444  case 'K':
2445  return(RailGraphics->CodeK);
2446 
2447  case 'L':
2448  return(RailGraphics->CodeL);
2449 
2450  case 'M':
2451  return(RailGraphics->CodeM);
2452 
2453  case 'N':
2454  return(RailGraphics->CodeN);
2455 
2456  case 'O':
2457  return(RailGraphics->CodeO);
2458 
2459  case 'P':
2460  return(RailGraphics->CodeP);
2461 
2462  case 'Q':
2463  return(RailGraphics->CodeQ);
2464 
2465  case 'R':
2466  return(RailGraphics->CodeR);
2467 
2468  case 'S':
2469  return(RailGraphics->CodeS);
2470 
2471  case 'T':
2472  return(RailGraphics->CodeT);
2473 
2474  case 'U':
2475  return(RailGraphics->CodeU);
2476 
2477  case 'V':
2478  return(RailGraphics->CodeV);
2479 
2480  case 'W':
2481  return(RailGraphics->CodeW);
2482 
2483  case 'X':
2484  return(RailGraphics->CodeX);
2485 
2486  case 'Y':
2487  return(RailGraphics->CodeY);
2488 
2489  case 'Z':
2490  return(RailGraphics->CodeZ);
2491 
2492  case 'a':
2493  return(RailGraphics->Code_a);
2494 
2495  case 'b':
2496  return(RailGraphics->Code_b);
2497 
2498  case 'c':
2499  return(RailGraphics->Code_c);
2500 
2501  case 'd':
2502  return(RailGraphics->Code_d);
2503 
2504  case 'e':
2505  return(RailGraphics->Code_e);
2506 
2507  case 'f':
2508  return(RailGraphics->Code_f);
2509 
2510  case 'g':
2511  return(RailGraphics->Code_g);
2512 
2513  case 'h':
2514  return(RailGraphics->Code_h);
2515 
2516  case 'i':
2517  return(RailGraphics->Code_i);
2518 
2519  case 'j':
2520  return(RailGraphics->Code_j);
2521 
2522  case 'k':
2523  return(RailGraphics->Code_k);
2524 
2525  case 'l':
2526  return(RailGraphics->Code_l);
2527 
2528  case 'm':
2529  return(RailGraphics->Code_m);
2530 
2531  case 'n':
2532  return(RailGraphics->Code_n);
2533 
2534  case 'o':
2535  return(RailGraphics->Code_o);
2536 
2537  case 'p':
2538  return(RailGraphics->Code_p);
2539 
2540  case 'q':
2541  return(RailGraphics->Code_q);
2542 
2543  case 'r':
2544  return(RailGraphics->Code_r);
2545 
2546  case 's':
2547  return(RailGraphics->Code_s);
2548 
2549  case 't':
2550  return(RailGraphics->Code_t);
2551 
2552  case 'u':
2553  return(RailGraphics->Code_u);
2554 
2555  case 'v':
2556  return(RailGraphics->Code_v);
2557 
2558  case 'w':
2559  return(RailGraphics->Code_w);
2560 
2561  case 'x':
2562  return(RailGraphics->Code_x);
2563 
2564  case 'y':
2565  return(RailGraphics->Code_y);
2566 
2567  case 'z':
2568  return(RailGraphics->Code_z);
2569 
2570  default:
2571  return(RailGraphics->TempHeadCode);
2572  }
2573 }
2574 
2575 // ----------------------------------------------------------------------------
2576 
2577 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2578 {
2579  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2580  if(Code.Length() != 4)
2581  {
2582  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2583  }
2584  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2585  {
2586  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2587  }
2588  if(BackgroundColour != clB5G5R5)
2589  // i.e. not the basic graphic colour as loaded from resource file
2590  {
2591  for(int x = 0; x < 4; x++)
2592  {
2594  }
2595  }
2596  Utilities->CallLogPop(1484);
2597 }
2598 
2599 // ----------------------------------------------------------------------------
2600 
2601 void TTrain::GetLeadElement(int Caller)
2602 // assumes Mid & Lag already set, sets LeadElement,
2603 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2604 {
2605  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2606  DerailPending = false;
2610  {
2611  // attr 0=straight, - links 0 & 1 (0 = lead)
2612  // attr 1=diverging, - links 2 & 3 (2 = lead)
2613  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2614  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2615 
2616  // if enter at lead, exit at whatever attr set at
2617  // if enter at lag, exit at lead, but set derail wrt attribute
2618  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2619  {
2620  LeadExitPos = 1;
2621  }
2622 
2623  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2624  // best to be on safe side
2625  else if(LeadEntryPos == 0)
2626  {
2627  LeadEntryPos = 2;
2628  LeadExitPos = 3;
2629  }
2630  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2631  {
2632  LeadEntryPos = 0;
2633  LeadExitPos = 1;
2634  }
2635  else if(LeadEntryPos == 2)
2636  {
2637  LeadExitPos = 3;
2638  }
2639  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2640  {
2641  LeadExitPos = 0;
2642  }
2643  else if(LeadEntryPos == 1)
2644  {
2645  LeadExitPos = 0;
2646  DerailPending = true;
2647  }
2648  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2649  {
2650  LeadExitPos = 0;
2651  DerailPending = true;
2652  }
2653  else if(LeadEntryPos == 3)
2654  {
2655  LeadExitPos = 0;
2656  }
2657  }
2658  else if(LeadEntryPos == 0)
2659  {
2660  LeadExitPos = 1;
2661  }
2662  else if(LeadEntryPos == 1)
2663  {
2664  LeadExitPos = 0;
2665  }
2666  else if(LeadEntryPos == 2)
2667  {
2668  LeadExitPos = 3;
2669  }
2670  else if(LeadEntryPos == 3)
2671  {
2672  LeadExitPos = 2;
2673  }
2674  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2675 /* signal check moved to Update() function
2676  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2677  && (TrackElement.Attribute == 0))//0 = red
2678  {
2679  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2680  }
2681  else
2682  {
2683  StoppedAtSignal = false;
2684  }
2685 */
2686  Utilities->CallLogPop(662);
2687 }
2688 
2689 // ----------------------------------------------------------------------------
2690 
2691 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2692 {
2693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2694  switch(Link)
2695  {
2696  case 1:
2697  {
2698  HOffset = 0;
2699  VOffset = 0;
2700  break;
2701  }
2702 
2703  case 2:
2704  {
2705  HOffset = 4;
2706  VOffset = 0;
2707  break;
2708  }
2709 
2710  case 3:
2711  {
2712  HOffset = 8;
2713  VOffset = 0;
2714  break;
2715  }
2716 
2717  case 4:
2718  {
2719  HOffset = 0;
2720  VOffset = 4;
2721  break;
2722  }
2723 
2724  case 6:
2725  {
2726  HOffset = 8;
2727  VOffset = 4;
2728  break;
2729  }
2730 
2731  case 7:
2732  {
2733  HOffset = 0;
2734  VOffset = 8;
2735  break;
2736  }
2737 
2738  case 8:
2739  {
2740  HOffset = 4;
2741  VOffset = 8;
2742  break;
2743  }
2744 
2745  case 9:
2746  {
2747  HOffset = 8;
2748  VOffset = 8;
2749  break;
2750  }
2751 
2752  default:
2753  {
2754  throw Exception("Error in GetOffsetValues - Link value wrong");
2755  }
2756  }
2757  Utilities->CallLogPop(674);
2758 }
2759 
2760 // ---------------------------------------------------------------------------
2761 
2762 bool TTrain::LowEntryValue(int EntryLink) const
2763 {
2764 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2765  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2766 */
2767  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2768  {
2769  return(true);
2770  }
2771  else
2772  {
2773  return(false);
2774  }
2775 }
2776 
2777 // ---------------------------------------------------------------------------
2778 
2779 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2780 {
2781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2782  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2783  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2784  // default values
2785  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2786 
2787  TAllRoutes::TRouteType RouteType;
2788 
2789  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2790 
2791  TRect SourceRect, DestRect;
2792 
2793  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2794  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2795  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2796  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2797 
2798  TempGraphic->PixelFormat = pf8bit;
2799  TempGraphic->Width = 16;
2800  TempGraphic->Height = 16;
2801  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2802 
2803  if(TempElement.TrackType == Points)
2804  {
2805  TempGraphic->Assign(TempElement.GraphicPtr);
2806  TempGraphic->Transparent = true;
2807  TempGraphic->TransparentColor = Utilities->clTransparent;
2808  if(RouteType == TAllRoutes::AutoSigsRoute)
2809  {
2810  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2811  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2812  }
2813  else
2814  {
2815  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2816  }
2817  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2818  }
2819  else if(TempElement.TrackType == GapJump) // plot set gap
2820  {
2821  if(TempElement.SpeedTag == 88)
2822  {
2823  TempGraphic->Assign(RailGraphics->gl88set);
2824  }
2825  else if(TempElement.SpeedTag == 89)
2826  {
2827  TempGraphic->Assign(RailGraphics->gl89set);
2828  }
2829  else if(TempElement.SpeedTag == 90)
2830  {
2831  TempGraphic->Assign(RailGraphics->gl90set);
2832  }
2833  else if(TempElement.SpeedTag == 91)
2834  {
2835  TempGraphic->Assign(RailGraphics->gl91set);
2836  }
2837  else if(TempElement.SpeedTag == 92)
2838  {
2839  TempGraphic->Assign(RailGraphics->gl92set);
2840  }
2841  else if(TempElement.SpeedTag == 93)
2842  {
2843  TempGraphic->Assign(RailGraphics->bm93set);
2844  }
2845  else if(TempElement.SpeedTag == 94)
2846  {
2847  TempGraphic->Assign(RailGraphics->bm94set);
2848  }
2849  else if(TempElement.SpeedTag == 95)
2850  {
2851  TempGraphic->Assign(RailGraphics->gl95set);
2852  }
2853  TempGraphic->Transparent = true;
2854  TempGraphic->TransparentColor = Utilities->clTransparent;
2855  if(RouteType == TAllRoutes::AutoSigsRoute)
2856  {
2857  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2858  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2859  }
2860  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2861  }
2862  // new for version 0.6
2863  else if(TempElement.TrackType == SignalPost)
2864  {
2865  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2866  {
2867  for(int x = 0; x < 40; x++)
2868  {
2869  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2870  // need to stop aspect
2871  {
2872  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2873  break;
2874  }
2875  }
2876  }
2877  else // normal signal
2878  {
2879  TempGraphic->Assign(TempElement.GraphicPtr);
2880  // GraphicPtr set to normal signal in a signal track element
2881  }
2882  TempGraphic->Transparent = true;
2883  TempGraphic->TransparentColor = Utilities->clTransparent;
2884  if(RouteType == TAllRoutes::AutoSigsRoute)
2885  {
2886  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2887  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2888  }
2889  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2890  }
2891  else
2892  {
2893  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2894  // can't name points gaps or signals so 'else' OK
2895  bool FoundFlag;
2896  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2897  if(FoundFlag)
2898  {
2900  {
2901  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2902  TempGraphic->Assign(RailGraphics->bmName);
2903  TempGraphic->Transparent = true;
2904  TempGraphic->TransparentColor = Utilities->clTransparent;
2905  if(RouteType == TAllRoutes::AutoSigsRoute)
2906  {
2907  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2908  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2909  }
2910  else
2911  {
2912  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2913  }
2914  // draw track on top
2915  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2916  }
2917  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2918  {
2919  TempGraphic->Assign(TempElement.GraphicPtr);
2920  TempGraphic->Transparent = true;
2921  TempGraphic->TransparentColor = Utilities->clTransparent;
2922  // note that can't be an AutoSigsRoute
2923  // now overlay the LC central portion
2924  int BDVectorPos = -1; //not used
2925  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2926  {
2927  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2928  }
2929  else
2930  {
2931  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2932  }
2933  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2934  }
2935  else
2936  {
2937  TempGraphic->Assign(TempElement.GraphicPtr);
2938  TempGraphic->Transparent = true;
2939  TempGraphic->TransparentColor = Utilities->clTransparent;
2940  if(RouteType == TAllRoutes::AutoSigsRoute)
2941  {
2942  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2943  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2944  }
2945  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2946  }
2947  }
2948  else
2949  {
2950  TempGraphic->Assign(TempElement.GraphicPtr);
2951  TempGraphic->Transparent = true;
2952  TempGraphic->TransparentColor = Utilities->clTransparent;
2953  if(RouteType == TAllRoutes::AutoSigsRoute)
2954  {
2955  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2956  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2957  }
2958  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2959  }
2960  }
2961  delete TempGraphic;
2962  Utilities->CallLogPop(675);
2963 }
2964 
2965 // ---------------------------------------------------------------------------
2966 
2967 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2968 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2969 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2970 /*
2971 
2972  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2973  {
2974  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2975  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2976  TAllRoutes::TRouteType RouteType;
2977  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2978  // default values
2979  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2980  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2981  TRect SourceRect, DestRect, ScreenSourceRect;
2982  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2983 
2984  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2985  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2986  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2987 
2988  //add text & user graphics if any to *GraphicPtr prior to adding the track
2989  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2990  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2991  int Right = Left + 8;
2992  int Bottom = Top + 8;
2993  ScreenSourceRect.init(Left, Top, Right, Bottom);
2994  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2995  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2996 
2997  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2998  TempGraphic->PixelFormat = pf8bit;
2999  TempGraphic->Width = 16;
3000  TempGraphic->Height = 16;
3001 
3002  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
3003  SourceGraphic->PixelFormat = pf8bit;
3004  SourceGraphic->Width = 16;
3005  SourceGraphic->Height = 16;
3006  SourceGraphic->Transparent = true;
3007  SourceGraphic->TransparentColor = Utilities->clTransparent;
3008 
3009  if (TempElement.TrackType == Points)
3010  {
3011  TempGraphic->Assign(TempElement.GraphicPtr);
3012  TempGraphic->Transparent = true;
3013  TempGraphic->TransparentColor = Utilities->clTransparent;
3014  if (RouteType == TAllRoutes::AutoSigsRoute)
3015  {
3016  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3017  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3018  }
3019  else
3020  {
3021  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3022  }
3023  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3024  }
3025  else if (TempElement.TrackType == GapJump) // plot set gap
3026  {
3027  if (TempElement.SpeedTag == 88)
3028  TempGraphic->Assign(RailGraphics->gl88set);
3029  else if (TempElement.SpeedTag == 89)
3030  TempGraphic->Assign(RailGraphics->gl89set);
3031  else if (TempElement.SpeedTag == 90)
3032  TempGraphic->Assign(RailGraphics->gl90set);
3033  else if (TempElement.SpeedTag == 91)
3034  TempGraphic->Assign(RailGraphics->gl91set);
3035  else if (TempElement.SpeedTag == 92)
3036  TempGraphic->Assign(RailGraphics->gl92set);
3037  else if (TempElement.SpeedTag == 93)
3038  TempGraphic->Assign(RailGraphics->bm93set);
3039  else if (TempElement.SpeedTag == 94)
3040  TempGraphic->Assign(RailGraphics->bm94set);
3041  else if (TempElement.SpeedTag == 95)
3042  TempGraphic->Assign(RailGraphics->gl95set);
3043  TempGraphic->Transparent = true;
3044  TempGraphic->TransparentColor = Utilities->clTransparent;
3045  if (RouteType == TAllRoutes::AutoSigsRoute) {
3046  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3047  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3048  }
3049  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3050  }
3051  // new for version 0.6
3052  else if (TempElement.TrackType == SignalPost)
3053  {
3054  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3055  {
3056  for (int x = 0; x < 40; x++)
3057  {
3058  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3059  // need to stop aspect
3060  {
3061  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3062  break;
3063  }
3064  }
3065  }
3066  else // normal signal
3067  {
3068  TempGraphic->Assign(TempElement.GraphicPtr);
3069  // GraphicPtr set to normal signal in a signal track element
3070  }
3071  TempGraphic->Transparent = true;
3072  TempGraphic->TransparentColor = Utilities->clTransparent;
3073  if (RouteType == TAllRoutes::AutoSigsRoute) {
3074  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3075  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3076  }
3077  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3078  }
3079  else {
3080  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3081  // can't name points gaps or signals so 'else' OK
3082  bool FoundFlag;
3083  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3084  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3085  if (FoundFlag)
3086  {
3087  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3088  {
3089  GraphicPtr->Canvas->CopyRect(DestRect,
3090  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3091  TempGraphic->Assign(RailGraphics->bmName);
3092  TempGraphic->Transparent = true;
3093  TempGraphic->TransparentColor = Utilities->clTransparent;
3094  if (RouteType == TAllRoutes::AutoSigsRoute)
3095  {
3096  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3097  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3098  }
3099  else
3100  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3101  // draw track on top
3102  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3103  SourceRect);
3104  }
3105  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3106  TempGraphic->Assign(TempElement.GraphicPtr);
3107  TempGraphic->Transparent = true;
3108  TempGraphic->TransparentColor = Utilities->clTransparent;
3109  // note that can't be an AutoSigsRoute
3110  // now overlay the LC central portion
3111  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3112  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3113  SourceRect);
3114  }
3115  else {
3116  TempGraphic->Assign(TempElement.GraphicPtr);
3117  TempGraphic->Transparent = true;
3118  TempGraphic->TransparentColor = Utilities->clTransparent;
3119  if (RouteType == TAllRoutes::AutoSigsRoute) {
3120  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3121  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3122  }
3123  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3124  SourceRect);
3125  }
3126  }
3127  else {
3128  TempGraphic->Assign(TempElement.GraphicPtr);
3129  TempGraphic->Transparent = true;
3130  TempGraphic->TransparentColor = Utilities->clTransparent;
3131  if (RouteType == TAllRoutes::AutoSigsRoute) {
3132  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3133  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3134  }
3135  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3136  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3137  }
3138  }
3139  delete TempGraphic;
3140  delete SourceGraphic;
3141  Utilities->CallLogPop();
3142  }
3143 */
3144 // ---------------------------------------------------------------------------
3145 
3146 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3147 {
3148  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3149  if(PlotElement[ArrayNumber] == -1)
3150  {
3151  Utilities->CallLogPop(676);
3152  return; // not plotted yet
3153  }
3154  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3155  // set before plot so gap flashing stops first
3156  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3157  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3158  // Only need to set ID for leading element, stays set until train finally leaves the element
3159  Plotted = true;
3160  Utilities->CallLogPop(677);
3161 }
3162 
3163 // ---------------------------------------------------------------------------
3164 
3165 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3166 {
3167  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3168  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3169 }
3170 
3171 // ---------------------------------------------------------------------------
3172 
3173 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3174 {
3175  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3176  HeadCode);
3177  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3178  {
3179  Utilities->CallLogPop(678);
3180  return(true);
3181  }
3182  else
3183  {
3184  Utilities->CallLogPop(679);
3185  return(false);
3186  }
3187 }
3188 
3189 // ---------------------------------------------------------------------------
3190 
3191 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3192 {
3193  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3194  "," + HeadCode);
3195  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3196  {
3197  Utilities->CallLogPop(680);
3198  return(true);
3199  }
3200  else
3201  {
3202  Utilities->CallLogPop(681);
3203  return(false);
3204  }
3205 }
3206 
3207 // ---------------------------------------------------------------------------
3208 
3209 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3210 // test whether this train on a bridge on trackpos 0 & 1
3211 {
3212  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3213  HeadCode);
3214  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3215  {
3216  Utilities->CallLogPop(682);
3217  return(false);
3218  }
3219  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3221  {
3223  {
3224  throw Exception("Error, same train on two different bridge tracks");
3225  }
3226  else
3227  {
3228  Utilities->CallLogPop(684);
3229  return(true);
3230  }
3231  }
3232  else
3233  {
3234  Utilities->CallLogPop(685);
3235  return(false);
3236  }
3237 }
3238 
3239 // ---------------------------------------------------------------------------
3240 
3241 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3242 // test whether this train on a bridge on trackpos 2 & 3
3243 {
3244  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3245  HeadCode);
3246  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3247  {
3248  Utilities->CallLogPop(686);
3249  return(false);
3250  }
3251  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3253  {
3254  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3255  Utilities->CallLogPop(687);
3256  return(true);
3257  }
3258  else
3259  {
3260  Utilities->CallLogPop(688);
3261  return(false);
3262  }
3263 }
3264 
3265 // ---------------------------------------------------------------------------
3266 
3267 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3268 {
3269  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3270  AnsiString(EntryPos) + "," + HeadCode);
3271  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3272 
3273  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3274  if(Track->GapFlashFlag)
3275  {
3277  {
3280  Track->GapFlashFlag = false;
3281  }
3282  }
3283  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3284  {
3285  if(EntryPos == -1)
3286  {
3287  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3288  }
3289  if(EntryPos < 2)
3290  {
3292  }
3293  else
3294  {
3296  }
3297  }
3298  Utilities->CallLogPop(690);
3299 }
3300 
3301 // ---------------------------------------------------------------------------
3302 
3303 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3304 {
3305  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3306  AnsiString(EntryPos) + "," + HeadCode);
3307  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3308  {
3309  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3310  }
3311  else
3312  {
3313  if(EntryPos == -1)
3314  {
3315  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3316  }
3317  if(EntryPos < 2)
3318  {
3319  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3320  }
3321  else
3322  {
3323  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3324  }
3325  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3326  // i.e. other train on track 2&3
3327  {
3328  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3329  }
3330  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3331  // i.e. other train on track 1&2
3332  {
3333  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3334  }
3335  else
3336  {
3337  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3338  }
3339  }
3340  Utilities->CallLogPop(691);
3341 }
3342 
3343 // ---------------------------------------------------------------------------
3344 
3345 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3346 {
3347  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3348  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3349  int LockedVectorNumber;
3350 
3351  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3352  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3353  {
3354  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3355  Utilities->CallLogPop(692);
3356  return;
3357  }
3358  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3359  // i.e other track is in a marked route
3360  // LinkPos doesn't have to be the entry position for the above check
3361  {
3362  TRect SourceRect, DestRect;
3363  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3364  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3365  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3366  // identify the route element for the other track
3367  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3368  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3369  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3370  int FirstELink, SecondELink = -1;
3371  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3372  // must be at least one
3373  if(RoutePair2.first > -1)
3374  {
3375  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3376  }
3377  TPrefDirElement RouteElement;
3378  // Graphics::TBitmap *RouteGraphic;
3379  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3380  // i.e. other track is in RoutePair2
3381  {
3382  if(SecondELink == -1)
3383  {
3384  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3385  }
3386  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3387  // error if both have same Link number
3388  {
3389  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3390  }
3391  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3392  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3393  }
3394  else // other track is in RoutePair1
3395  {
3396  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3397  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3398  }
3399  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3400  DestGraphic->PixelFormat = pf8bit;
3401  DestGraphic->Width = 8;
3402  DestGraphic->Height = 8;
3403  DestGraphic->Transparent = true;
3404  // has to be transparent or will overwrite the track that the train has just left
3405  DestGraphic->TransparentColor = Utilities->clTransparent;
3406  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3407  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3408  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3409  // plot locked route marker for other route if appropriate
3410  TPrefDirElement PrefDirElement; // holder for next call, unused
3411  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3412  if(StraddleValue == LeadMidLag)
3413  {
3415  PrefDirElement, LockedVectorNumber))
3416  {
3417  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3418  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3419  }
3420  }
3421  delete DestGraphic;
3422  }
3423  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3424  // also can only be a bridge or trains either have already or soon will crash
3425  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3426  {
3427  Utilities->CallLogPop(695);
3428  return;
3429  }
3430  if(ElementEntryPos > 1) // other train is on track 01
3431  {
3433  {
3435  }
3436  }
3437  else // other train is on track 23
3438  {
3440  {
3442  }
3443  }
3444  Utilities->CallLogPop(696);
3445 }
3446 
3447 // ---------------------------------------------------------------------------
3448 
3449 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3450 {
3451  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3452  AnsiString(EntryPos) + "," + HeadCode);
3453  int RouteNumber;
3454  bool WrongRoute = false;
3455  TPrefDirElement RouteElement;
3457  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3458 
3459  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3460  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3461  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3462  {
3463  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3464  {
3465  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3466  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3467  {
3468  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3469  {
3470  // don't call for stub end routes
3472  }
3473  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3474  Utilities->CallLogPop(697);
3475  return;
3476  }
3477  }
3478  // also need to check for a route on a crossing diagonal
3479  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3480  int LinkNumber = TrackElement.Link[EntryPos];
3481  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3482  {
3483  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3484  {
3485  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3486  bool LogActionErrorCalled = false;
3487  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3488  if(LinkNumber == 1)
3489  {
3490  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3491  {
3492  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3493  {
3494  // don't call for stub end routes
3496  LogActionErrorCalled = true;
3497  }
3498  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3499  }
3500  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3501  // not else in case have different routes on each diagonal, though shouldn't be possible
3502  {
3503  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3504  {
3505  // don't call for stub end routes
3507  }
3508  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3509  }
3510  }
3511 
3512  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3513  else if(LinkNumber == 3)
3514  {
3515  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3516  {
3517  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3518  {
3519  // don't call for stub end routes
3521  LogActionErrorCalled = true;
3522  }
3523  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3524  }
3525  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3526  // not else in case have different routes on each diagonal, though shouldn't be possible
3527  {
3528  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3529  {
3530  // don't call for stub end routes
3532  }
3533  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3534  }
3535  }
3536 
3537  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3538  else if(LinkNumber == 7)
3539  {
3540  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3541  {
3542  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3543  {
3544  // don't call for stub end routes
3546  LogActionErrorCalled = true;
3547  }
3548  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3549  }
3550  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3551  // not else in case have different routes on each diagonal, though shouldn't be possible
3552  {
3553  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3554  {
3555  // don't call for stub end routes
3557  }
3558  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3559  }
3560  }
3561 
3562  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3563  else if(LinkNumber == 9)
3564  {
3565  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3566  {
3567  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3568  {
3569  // don't call for stub end routes
3571  LogActionErrorCalled = true;
3572  }
3573  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3574  }
3575  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3576  // not else in case have different routes on each diagonal, though shouldn't be possible
3577  {
3578  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3579  {
3580  // don't call for stub end routes
3582  }
3583  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3584  }
3585  }
3586  }
3587  }
3588  Utilities->CallLogPop(698);
3589  return; // no route on other track or no other track
3590  }
3591  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3592  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3593  {
3594  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3595  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3596  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3597  {
3598  if(RouteElement.GetELinkPos() == EntryPos)
3599  {
3600  Utilities->CallLogPop(699);
3601  return; // right direction
3602  }
3603  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3604  {
3605  Utilities->CallLogPop(700);
3606  return; // right direction (points)
3607  }
3608  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3609  {
3610  Utilities->CallLogPop(701);
3611  return; // right direction (points)
3612  }
3613  else if(RouteElement.GetXLinkPos() == EntryPos)
3614  {
3615  WrongRoute = true;
3616  break; // wrong direction
3617  }
3618  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3619  {
3620  WrongRoute = true;
3621  break; // wrong direction
3622  }
3623  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3624  {
3625  WrongRoute = true;
3626  break; // wrong direction
3627  }
3628  }
3629  }
3630  if(!WrongRoute)
3631  {
3632  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3633  }
3634  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3635  {
3636  // don't call for stub end routes
3638  }
3639  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3640  Utilities->CallLogPop(703);
3641 }
3642 
3643 // ---------------------------------------------------------------------------
3644 
3645 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3646 {
3647  if(BackgroundColour == NewBackgroundColour)
3648  {
3649  return; // don't replot if already correct
3650 
3651  }
3652  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3653  bool ColourError = false, ColourError2 = false;
3654 
3655  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3656  if(ColourError)
3657  {
3658  ColourError2 = true;
3659  }
3660  for(int x = 0; x < 4; x++)
3661  {
3662  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3663  if(ColourError)
3664  {
3665  ColourError2 = true;
3666  }
3667  }
3668  if(ColourError2)
3669  {
3671  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3672  }
3673  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3674  // of motion
3675  for(int x = 0; x < 4; x++)
3676  {
3677  PlotTrainGraphic(6, x, Disp);
3678  }
3679  BackgroundColour = NewBackgroundColour;
3680  Display->Update();
3681  // need to keep this since Update() not called for PlotSmallOutput as too slow
3682  Utilities->CallLogPop(704);
3683 }
3684 
3685 // ---------------------------------------------------------------------------
3686 
3687 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3688 /*
3689 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3690 
3691 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3692 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3693 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3694 full-element moves.
3695 
3696 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3697 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3698 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3699 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3700 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3701 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3702 In this case set the brake rate to maximum to stop as soon as possible.
3703 
3704 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3705 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3706 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3707 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3708 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3709 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3710 
3711 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3712 first to see whether buffers or continuation) in turn is examined: first the length of the
3713 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3714 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3715 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3716 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3717 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3718 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3719 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3720 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3721 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3722 siding then again emeregency braking may be necessary and a crash may result.
3723 
3724 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3725 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3726 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3727 buffer, then the train accelerates for half the element and brakes for the other half.
3728 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3729 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3730 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3731 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3732 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3733 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3734 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3735 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3736 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3737 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3738 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3739 
3740 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3741 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3742 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3743 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3744 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3745 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3746 MaxBrakeRate.
3747 
3748 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3749 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3750 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3751 
3752 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3753 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3754 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3755 
3756 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3757 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3758 when Straddle == LeadMidLag
3759 */
3760 {
3761  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3762  AnsiString(EntryPos) + "," + HeadCode);
3763  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3764  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3765  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3766  TrainInFrontInSignallerModeFlag = false;
3767  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3768  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3769  bool SignallerStopRequired = false;
3770 
3772  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3773 
3774  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3775 
3776  OneLengthAccelDecel = false;
3777  BrakeRate = 0;
3778 
3779 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3780  if(CurrentTrackVectorPosition > -1)
3781  {
3782  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3783  {
3784  if((EntryPos == 0) || (EntryPos == 2))
3785  {
3786  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3787  {
3788  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3789  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3790  }
3791  else
3792  {
3793  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3794  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3795  }
3796  }
3797  else if(EntryPos == 1)
3798  {
3799  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3800  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3801  }
3802  else // == 3
3803  {
3804  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3805  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3806  }
3807  }
3808  else
3809  {
3810  if(EntryPos > 1)
3811  {
3812  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3813  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3814  }
3815  else
3816  {
3817  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3818  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3819  }
3820  }
3821  EntryHalfLength = CurrentElementHalfLength;
3822  FrontElementLength = 2 * CurrentElementHalfLength;
3823  }
3824  else
3825  {
3826  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3827  }
3828  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3829  {
3830  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3831  }
3832  // check if zero entry speed with another train directly in front & if so remain stopped
3833  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3834  {
3835  EntrySpeed = 0;
3836  ExitSpeedHalf = 0;
3837  ExitSpeedFull = 0;
3838  MaxExitSpeed = 0;
3839  BrakeRate = 0;
3840  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3841  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3842  StoppedForTrainInFront = true;
3843  Utilities->CallLogPop(705);
3844  return;
3845  }
3846  // new at v2.4.0 - check for stopped and zero power
3847  if((EntrySpeed < 1) && PowerAtRail < 1)
3848  {
3849  EntrySpeed = 0;
3850  ExitSpeedHalf = 0;
3851  ExitSpeedFull = 0;
3852  MaxExitSpeed = 0;
3853  BrakeRate = 0;
3854  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3855  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3856  StoppedWithoutPower = true;
3857  Utilities->CallLogPop(2125);
3858  return;
3859  }
3860 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3861  if(BeingCalledOn)
3862  {
3863  LimitingSpeed = CallOnMaxSpeed;
3864  }
3865  else
3866  {
3867  LimitingSpeed = MaximumSpeedLimit;
3868  }
3869  if(LimitingSpeed > FrontElementSpeedLimit)
3870  {
3871  LimitingSpeed = FrontElementSpeedLimit;
3872  }
3873  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3874  {
3875  LimitingSpeed = MaxRunningSpeed;
3876  }
3877  FrontElementMaxSpeed = LimitingSpeed;
3878 
3879 /*
3880  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3881  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3882  (2) V/3.6 = U/3.6 - FT;
3883  (3) S = UT/3.6 - 0.5FT^2
3884  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3885 
3886  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3887  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3888  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3889  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3890  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3891 
3892  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3893  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3894  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3895 */
3896 
3897 // check if running past a red signal without permission
3898  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) &&
3899  (Track->TrackElementAt(353, CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal &&
3900  !Track->TrackElementAt(1553, CurrentTrackVectorPosition).CallingOnSet)
3901  { //CallingOnSet added at v2.14.0
3902  SPADFlag = true; // user has to intervene to reset & restart after spad
3903  }
3904  if(!SPADFlag)
3905  {
3906  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3907  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3908  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3909  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3910  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3911  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3912 
3913  double ExitSpeedAtMaxBraking;
3914  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3915  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3917  {
3918  ExitSpeedAtMaxBraking = 0;
3919  }
3920  else
3921  {
3922  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3923  }
3924  double SpeedToUse;
3925  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3926  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3927  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3928  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3929  {
3930  SpeedToUse = ExitSpeedAtMaxBraking;
3931  }
3932  else
3933  {
3934  SpeedToUse = LimitingSpeed;
3935  }
3936  if(ExitSpeedFull > SpeedToUse)
3937  {
3938  ExitSpeedFull = SpeedToUse;
3939  }
3940  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3941  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3942 
3943  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3944  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3945  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3946 
3947  do
3948  {
3949  RedSignalFlag = false;
3950  BuffersFlag = false;
3951  StationFlag = false;
3952  BuffersOrContinuationNowFlag = false;
3953  ContinuationNextFlag = false;
3954  // have to reset this after the above test
3955  // add current element length to CumulativeLength
3956  CumulativeLength += (2 * CurrentElementHalfLength);
3957  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3958  {
3959  SignallerStopRequired = true;
3960  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3961  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3962  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3963  if(SignallerStopBrakeRate < TempBR)
3964  {
3965  SignallerStopBrakeRate = TempBR;
3966  }
3967  }
3968  // first check for stops within the length of the current element, where don't want any more checks & don't want
3969  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3970  // during the last loop when the NextTrackVectorPosition was the signal.
3971 
3972  // check if current element is a buffer
3973  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3974  {
3975  // no need to add in the length of this element to CumulativeLength as already included
3976  BuffersFlag = true;
3977  }
3978  // check if current element is a station stop
3979  if(TrainMode == Timetable)
3980  {
3981  bool StopRequired = false;
3982  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3983  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3984  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3985  {
3986  // no need to add in the length of element to CumulativeLength
3987  if(StopRequired)
3988  {
3989  StationFlag = true;
3990  }
3991  }
3992  }
3993  else
3994  {
3995  StationFlag = false;
3996  }
3997  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3998  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3999  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4000  {
4001  BuffersOrContinuationNowFlag = true;
4002  }
4003  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
4004  {
4005  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
4006  {
4007  if((EntryPos == 0) || (EntryPos == 2))
4008  {
4009  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
4010  {
4011  ExitPos = 1;
4012  }
4013  else
4014  {
4015  ExitPos = 3;
4016  }
4017  }
4018  else
4019  {
4020  ExitPos = 0;
4021  }
4022  }
4023  else
4024  {
4025  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4026  }
4027  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4028  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4029  if(NextTrackVectorPosition > -1)
4030  {
4031  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4032  // this test & section added at v0.6
4033  {
4034  if((NextEntryPos == 0) || (NextEntryPos == 2))
4035  {
4036  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4037  {
4038  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4039  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4040  }
4041  else
4042  {
4043  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4044  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4045  }
4046  }
4047  else if(NextEntryPos == 1)
4048  {
4049  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4050  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4051  }
4052  else // == 3
4053  {
4054  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4055  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4056  }
4057  }
4058  else
4059  {
4060  if(NextEntryPos > 1)
4061  {
4062  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4063  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4064  }
4065  else
4066  {
4067  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4068  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4069  }
4070  }
4071  }
4072  else
4073  {
4074  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4075  }
4076  // now check for stops, first cover those where don't want to add in length of next element
4077  // check if next element is a red signal - Attr 0,
4078  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4079  // CurrentTrackVectorPosition not NextTrackVectorPosition
4080  bool StopRequired;
4081  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4082  {
4083  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4084  {
4085  // no need to add in the length of element to CumulativeLength
4086  RedSignalFlag = true;
4087  }
4088  // next element is a red signal
4089  }
4090  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4091  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4092  // at least one platform element free
4094  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
4095  NextEntryPos, TrainID))
4096  {
4097  // no need to add in the length of element to CumulativeLength
4098  if(StopRequired)
4099  {
4100  StationFlag = true;
4101  }
4102  }
4103  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4104  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4105  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4106  {
4107  // no need to add in the length of element to CumulativeLength
4108  TrainInFrontInSignallerModeFlag = true;
4109  }
4110  // check if next element is a buffer
4111  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
4112  {
4113  // need to add in the length of that element to CumulativeLength
4114  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4115  BuffersFlag = true;
4116  }
4117  // check if next element is a station stop
4119  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4120  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4121  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
4122  {
4123  // need to add in the length of that element to CumulativeLength if a stop required
4124  if(StopRequired)
4125  {
4126  StationFlag = true;
4127  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4128  }
4129  }
4130  }
4131  //now can decide whether need to stop over CumulativeLength
4132  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4133  {
4134  // have to come to a stop over CumulativeLength
4135  if(CumulativeLength == FrontElementLength)
4136  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4137  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4138  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4139  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4140  // if not calc speed at halfway point & if less than above set half speed to this value;
4141  // use constant acceleration in calculating half time point
4142  {
4143  MaxExitSpeed = 0;
4144  double MaxHalfSpeed;
4145  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4146  // have to halve the element length, & can't be zero or negative so no need to test
4147  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4148  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4149  {
4150  MaxHalfSpeed = FrontElementMaxSpeed;
4151  }
4152  else
4153  {
4154  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4155  }
4156  if(MaxHalfSpeed > (2 * EntrySpeed))
4157  // use 2x to prevent kangarooing at last element when had
4158  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4159  {
4160  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4161  0.333334);
4162  bool HalfSpeedLimited = false;
4163  if(MaxHalfSpeed < ExitSpeedHalf)
4164  {
4165  ExitSpeedHalf = MaxHalfSpeed;
4166  HalfSpeedLimited = true;
4167  }
4168  if(PowerAtRail > 1)
4169  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4170  {
4171  // [km/h/3.6 = m/s]
4172  ExitTimeHalf =
4173  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4174  }
4175  else
4176  {
4177  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4178  }
4179  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4180  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4181  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4182  // by a long braking period
4183  ExitSpeedFull = 0;
4184  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4185  if(TempBrakeRate > MaxBrakeRate)
4186  {
4187  TempBrakeRate = MaxBrakeRate;
4188  }
4189  // shouldn't be but leave in anyway
4190  if(TempBrakeRate > BrakeRate)
4191  {
4192  BrakeRate = TempBrakeRate;
4193  }
4194  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4195  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4196  if(HalfSpeedLimited)
4197  // this is the change referred to above
4198  {
4199  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4200  ExitTimeHalf = EntryTime + BrakingTime;
4201  ExitTimeFull = ExitTimeHalf + BrakingTime;
4202  }
4203  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4204  Utilities->CallLogPop(1095);
4205  return;
4206  }
4207  }
4208  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4209  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4210  // calc th, tf, sh, & sf
4211  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4212  if(TempBrakeRate > MaxBrakeRate)
4213  {
4214  TempBrakeRate = MaxBrakeRate;
4215  }
4216  if(TempBrakeRate > BrakeRate)
4217  {
4218  BrakeRate = TempBrakeRate;
4219  }
4220  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4221  if(SignallerStopRequired)
4222  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4223  {
4225  {
4227  // this prevents the brakerate from reducing for a signaller stop
4228  // regardless of other conditions that may change as progress round the loop
4229  }
4230  }
4232  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4233  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4234  {
4236  }
4237  int TempMaxExitSpeed;
4238  // calc current value & if less than MaxExitSpeed set that to this
4239  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4240  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4241  {
4242  MaxExitSpeedAtHalfBraking = 0;
4243  }
4244  else
4245  {
4246  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4247  }
4248  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4249  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4250  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4251  {
4252  TempMaxExitSpeed = FrontElementMaxSpeed;
4253  }
4254  else
4255  {
4256  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4257  }
4258  if(TempMaxExitSpeed < MaxExitSpeed)
4259  {
4260  MaxExitSpeed = TempMaxExitSpeed;
4261  }
4262  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4263  // Cumulativelength, and Cumulativelength
4264 
4265  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4266  {
4267  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4268  if(ExitSpeedHalfSquared < 10)
4269  {
4270  ExitSpeedHalf = 0;
4271  }
4272  else
4273  {
4274  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4275  }
4276  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4277  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4278  if(ExitSpeedFullSquared < 10)
4279  {
4280  ExitSpeedFull = 0;
4281  }
4282  else
4283  {
4284  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4285  }
4286  if((StationFlag) && (CumulativeLength == FrontElementLength))
4287  {
4288  ExitSpeedFull = 0;
4289  // force a stop for station (not for buffers or red signal)
4290  }
4291  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4292  }
4293  // new condition at v2.4.0
4294  else if(PowerAtRail <= 1)
4295  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4296  // avoid using AValue in denominator or have excessively long times
4297  {
4298  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4299  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4300  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4301 
4302  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4303  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4304  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4305  }
4306  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4307  // without the power need above condition or have hours of delay times, above added at v2.4.0
4308  {
4309  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4310  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4311  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4312  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4313  BrakeRate = 0;
4314  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4315  0.333334);
4316  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4317  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4319  // can accelerate continually over the half length
4320  {
4321  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4322  / 86400.0);
4324  // can accelerate continually over the full length
4325  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4326  {
4327  ExitTimeFull =
4328  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4329  }
4330  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4331  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4332  {
4333  // added at v0.6 as a safeguard
4334  if(MaxExitSpeed < EntrySpeed)
4335  {
4337  }
4338  // to prevent DeltaExitTimeToMaxInSecs being negative
4339  if(MaxExitSpeed < 1)
4340  {
4341  MaxExitSpeed = 1;
4342  }
4343  // to prevent divide by zero error
4344  // below as was
4346  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4347  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4348  (1.5 * AValue * AValue);
4349  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4350  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4351  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4352  }
4353  }
4354  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4355  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4356  // second halves of the element
4357  {
4358  // added at v0.6 as a safeguard
4359  if(MaxExitSpeed < EntrySpeed)
4360  {
4362  }
4363  // to prevent DeltaExitTimeToMaxInSecs being negative
4364  if(MaxExitSpeed < 1)
4365  {
4366  MaxExitSpeed = 1; // to prevent divide by zero error
4367  }
4368  // below as was
4370  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4371  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4372  (1.5 * AValue * AValue);
4373  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4374  // remaining distance to half length
4375  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4376  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4378  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4379  }
4380  }
4381  Utilities->CallLogPop(706);
4382  return;
4383  }
4384  else
4385  {
4386  if(!BuffersOrContinuationNowFlag)
4387  {
4388  if(NextSpeedLimit < LimitingSpeed)
4389  {
4390  LimitingSpeed = NextSpeedLimit;
4391  }
4392  }
4393  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4394  int TempMaxExitSpeed;
4395  // calc current value & if less than MaxExitSpeed set that to this
4396  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4397  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4398  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4399  {
4400  MaxExitSpeedAtHalfBraking = 0;
4401  }
4402  else
4403  {
4404  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4405  }
4406  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4407  {
4408  TempMaxExitSpeed = FrontElementMaxSpeed;
4409  }
4410  else
4411  {
4412  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4413  }
4414  if(TempMaxExitSpeed < MaxExitSpeed)
4415  {
4416  MaxExitSpeed = TempMaxExitSpeed;
4417  }
4418  // MaxExitSpeed is an external variable & this can reduce its value
4419  if(EntrySpeed > LimitingSpeed)
4420  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4421  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4422  {
4423  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4424  if(TempBrakeRate > MaxBrakeRate)
4425  {
4426  TempBrakeRate = MaxBrakeRate;
4427  }
4428  // shouldn't be for speedlimits since all known in advance, but include anyway
4429  if(TempBrakeRate > BrakeRate)
4430  {
4431  BrakeRate = TempBrakeRate;
4432  }
4433  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4434  }
4435  }
4436  if(!BuffersOrContinuationNowFlag)
4437  {
4438  CurrentTrackVectorPosition = NextTrackVectorPosition;
4439  EntryPos = NextEntryPos;
4440  CurrentElementHalfLength = NextElementHalfLength;
4441  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4442  {
4443  ContinuationNextFlag = true;
4444  }
4445  }
4446  }
4447  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4449  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4450  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4451  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4452  // stopping distance after it.
4453 
4454  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4455  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4456 
4457  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4458  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4459  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4460  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4461  // too late
4462 
4463  // set final braking or acc'n speed & time values
4464  if(BrakeRate > 0.01)
4465  {
4466  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4467  if(ExitSpeedHalfSquared < 10)
4468  {
4469  ExitSpeedHalf = 0;
4470  }
4471  else
4472  {
4473  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4474  }
4475  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4476  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4477  if(ExitSpeedFullSquared < 10)
4478  {
4479  ExitSpeedFull = 0;
4480  }
4481  else
4482  {
4483  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4484  }
4485  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4486  }
4487  else
4488  {
4489  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4490  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4491  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4492  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4493 
4494  BrakeRate = 0;
4495  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4496  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4497  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4499  {
4500  if(PowerAtRail > 1)
4501  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4502  {
4503  // [km/h/3.6 = m/s]
4504  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4505  / 86400.0);
4506  }
4507  else
4508  {
4509  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4510  }
4512  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4513  {
4514  if(PowerAtRail > 1)
4515  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4516  {
4517  // [km/h/3.6 = m/s]
4518  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4519  / 86400.0);
4520  }
4521  else
4522  {
4523  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4524  }
4525  }
4526  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4527  {
4528  // added at v0.6 as a safeguard
4529  if(MaxExitSpeed < EntrySpeed)
4530  {
4532  }
4533  // to prevent DeltaExitTimeToMaxInSecs being negative
4534  if(MaxExitSpeed < 1)
4535  {
4536  MaxExitSpeed = 1; // to prevent divide by zero error
4537  }
4538  // below as was
4540  double DeltaExitTimeToMaxInSecs;
4541  double DistanceToMax;
4542  if(PowerAtRail > 1) // added at v2.4.0
4543  {
4544  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4545  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4546  (1.5 * AValue * AValue);
4547  }
4548  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4549  {
4550  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4551  // these not really accurate but will be good enough
4552  DistanceToMax = EntryHalfLength;
4553  }
4554  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4555  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4556  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4557  }
4558  }
4559  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4560  {
4561  // added at v0.6 as a safeguard
4562  if(MaxExitSpeed < EntrySpeed)
4563  {
4565  }
4566  // to prevent DeltaExitTimeToMaxInSecs being negative
4567  if(MaxExitSpeed < 1)
4568  {
4569  MaxExitSpeed = 1; // to prevent divide by zero error
4570  }
4571  // below as was
4573  double DeltaExitTimeToMaxInSecs;
4574  double DistanceToMax;
4575  if(PowerAtRail > 1) // added at v2.4.0
4576  {
4577  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4578  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4579  (1.5 * AValue * AValue);
4580  }
4581  else
4582  {
4583  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4584  // these not really accurate but will be good enough
4585  DistanceToMax = EntryHalfLength / 2;
4586  }
4587  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4588  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4589  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4591  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4592  }
4593  }
4594  }
4595 
4596  else // SPADFlag set
4597  {
4599  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4600  if(ExitSpeedHalfSquared < 10)
4601  {
4602  ExitSpeedHalf = 0;
4603  }
4604  else
4605  {
4606  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4607  }
4608  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4609  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4610  if(ExitSpeedFullSquared < 10)
4611  {
4612  ExitSpeedFull = 0;
4613  }
4614  else
4615  {
4616  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4617  }
4618  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4619 
4620  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4621  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4622  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4623  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4624  // will be the stopping speed.
4625  if(ExitSpeedFull > 0)
4626  {
4627  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4628  {
4629  if((EntryPos == 0) || (EntryPos == 2))
4630  {
4631  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4632  {
4633  ExitPos = 1;
4634  }
4635  else
4636  {
4637  ExitPos = 3;
4638  }
4639  }
4640  else
4641  {
4642  ExitPos = 0;
4643  }
4644  }
4645  else
4646  {
4647  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4648  }
4649  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4650  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4651  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4652  {
4653  int NextElementLength;
4654  if(NextEntryPos > 1)
4655  {
4656  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4657  }
4658  else
4659  {
4660  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4661  }
4662  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4663  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4664  {
4665  ExitSpeedFull = 0;
4666  }
4667  }
4668  }
4669  }
4670  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4671  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4672  {
4673  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4674  if(LeadElement > -1)
4675  {
4677  // don't stop on a continuation either entering or leaving
4678  {
4681  MaxExitSpeed = LimitingSpeed;
4682  BrakeRate = 0;
4683  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4684  // assume length is 50m for ease of calc
4685  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4686  Utilities->CallLogPop(2126);
4687  return;
4688  }
4689  }
4690  else if(MidElement > -1)
4691  {
4693  {
4696  MaxExitSpeed = LimitingSpeed;
4697  BrakeRate = 0;
4698  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4699  // assume length is 50m for ease of calc
4700  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4701  Utilities->CallLogPop(2127);
4702  return;
4703  }
4704  }
4705  else if(LagElement > -1)
4706  {
4708  {
4711  MaxExitSpeed = LimitingSpeed;
4712  BrakeRate = 0;
4713  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4714  // assume length is 50m for ease of calc
4715  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4716  Utilities->CallLogPop(2128);
4717  return;
4718  }
4719  }
4720  if(EntrySpeed > 7.5) // keep going for at least another element
4721  {
4722  ExitSpeedHalf = EntrySpeed - 2.5;
4723  ExitSpeedFull = EntrySpeed - 5;
4724  MaxExitSpeed = LimitingSpeed;
4725  BrakeRate = 0;
4726  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4727  // assume length is 50m for ease of calc
4728  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4729  Utilities->CallLogPop(2129);
4730  return;
4731  }
4732  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4733  {
4734  // will appear to have slowed at steady rate
4735  ExitSpeedHalf = 0;
4736  ExitSpeedFull = 0;
4737  MaxExitSpeed = LimitingSpeed;
4738  BrakeRate = 0;
4739  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4740  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4741  StoppedWithoutPower = true;
4742  Utilities->CallLogPop(2130);
4743  return;
4744  }
4745  }
4746  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4747  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4748  Utilities->CallLogPop(707);
4749 }
4750 // ---------------------------------------------------------------------------
4751 /*
4752  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4753  {
4754  int NextExitPos;
4755  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4756  if(TimetableVector.empty()) return false;
4757  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4758  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4759  {
4760  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4761  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4762  {
4763  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4764  }
4765  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4766  {
4767  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4768  }
4769  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4770  NextElement = TempElement;
4771  }
4772  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4773  if(NextElement.TrackType == Buffers) return true;
4774  return false;//shouldn't reach here but include to prevent compiler return warning
4775  }
4776 */
4777 // ---------------------------------------------------------------------------
4778 
4779 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4780 /*
4781  returns the number by which the train ActionVectorEntryPtr needs
4782  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4783  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4784  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4785  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4786  or pass (false) the location.
4787 */{
4788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4789  Stop = false;
4790  if(TimetableFinished || (Name == ""))
4791  {
4792  Utilities->CallLogPop(957);
4793  return(-1);
4794  }
4796 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4797 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4798 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4799 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4800 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4801 */
4802  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4803  {
4804  Ptr--;
4805  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4806  {
4807  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4808  {
4809  Utilities->CallLogPop(2444);
4810  return(-1);
4811  }
4812  }
4813  }
4814  // start looking from current pointer position
4815  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4816  {
4817  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4818  {
4819  break;
4820  }
4821  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4822  {
4823  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4824  {
4825  Stop = true;
4826  Utilities->CallLogPop(960);
4827  return (Ptr - ActionVectorEntryPtr);
4828  }
4829  }
4830  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4831  {
4832  Utilities->CallLogPop(1517);
4833  return (Ptr - ActionVectorEntryPtr);
4834  }
4835  }
4836  Utilities->CallLogPop(959);
4837  return(-1); // not found a valid entry
4838 }
4839 
4840 // ---------------------------------------------------------------------------
4841 
4843 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4844  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4845  Ignores the call-on signal.
4846 */{
4847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4848  int ReturnVal = 0;
4849  int ElementCount = 0;
4850 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4851  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4852  {
4853  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4854  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4855  }
4856 */
4857  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4858  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4859 
4860  while(true)
4861  {
4862  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4863  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4864  {
4865  ReturnVal = 1;
4866  break;
4867  }
4868  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4869  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4870  {
4871  ReturnVal = 2;
4872  break;
4873  }
4874  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4875  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4876  {
4877  ReturnVal = 3;
4878  break;
4879  }
4880 /* not needed at and after v2.12.0, see above
4881  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4882  {
4883  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4884  // must be a loop - reached same point as examined earlier
4885  {
4886  ReturnVal = 4;
4887  break;
4888  }
4889  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4890  {
4891  ReturnVal = 4;
4892  break;
4893  }
4894  }
4895  else
4896  {
4897  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4898  {
4899  ReturnVal = 4;
4900  break;
4901  }
4902  }
4903  if(EntryPos < 2)
4904  {
4905  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4906  }
4907  else
4908  {
4909  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4910  }
4911 */
4912 
4913  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4914  {
4915  if((EntryPos == 0) || (EntryPos == 2))
4916  {
4917  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4918  {
4919  ExitPos = 1;
4920  }
4921  else
4922  {
4923  ExitPos = 3;
4924  }
4925  }
4926  else
4927  {
4928  ExitPos = 0;
4929  }
4930  }
4931  else
4932  {
4933  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4934  }
4935  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4936  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4937  CurrentTrackVectorPosition = NextTrackVectorPosition;
4938  EntryPos = NextEntryPos;
4939  ElementCount++;
4940  if(ElementCount > 1000)
4941  {
4942  ReturnVal = 4;
4943  break;
4944  }
4945  }
4946  if(ReturnVal == 1)
4947  {
4948  Utilities->CallLogPop(708);
4949  return(false);
4950  }
4951  if(ReturnVal == 2)
4952  {
4953  Utilities->CallLogPop(709);
4954  return(true);
4955  }
4956  if(ReturnVal == 3)
4957  {
4958  Utilities->CallLogPop(946);
4959  return(true);
4960  }
4961  if(ReturnVal == 4)
4962  {
4963  Utilities->CallLogPop(947);
4964  return(true);
4965  }
4966  else
4967  {
4968  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4969  }
4970 }
4971 
4972 // ---------------------------------------------------------------------------
4973 
4975 /*
4976  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4977  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4978  change of direction (cdt), remaining here (Frh), or under signaller control);
4979  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4980  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4981  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4982  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4983  m) not failed or stopped without power
4984  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4985  change points outside the route or have a route conflict if another route is set.
4986 */{
4987  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4988  {
4989  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4990  }
4991  // some of the callingon route elements may be involved
4992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4993  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4994  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4995  int RouteStartPosition;
4996  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4997  int PlatformPosition;
4998  // the track vector position of the first stop platfrom
4999  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
5000  // not used here
5001  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
5002  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
5003  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
5004  // must be stopped at a signal but not at a location & still in timetable (a)
5006  // no need to check for SignallerStopped as this function only called in Timetable mode
5007  {
5008  Utilities->CallLogPop(711);
5009  return(false);
5010  }
5011  while(true)
5012  {
5013  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
5014  // don't look further than 4km ahead (j)
5015  if(Distance > (4000 + LeadElementDistance))
5016  {
5017  Utilities->CallLogPop(967);
5018  return(false);
5019  }
5020  // if find another train on an element in front, before find a valid platform, return false (c)
5021  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
5022  {
5023  Utilities->CallLogPop(713);
5024  return(false);
5025  }
5026  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5027  // be facing later on)
5028  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5029  {
5030  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5031  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5032  if(OtherTrain.LeadElement == -1)
5033  {
5034  Utilities->CallLogPop(714);
5035  return(false);
5036  }
5037  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5038  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5039  {
5040  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5041  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5042  (OtherTrain.TrainMode == Signaller))
5043  {
5044  break;
5045  }
5046  else
5047  {
5048  Utilities->CallLogPop(955);
5049  return(false);
5050  }
5051  }
5052  else // (h)
5053  {
5054  break;
5055  }
5056  }
5057  // if reach buffers or exit continuation return false (can set route)
5058  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5059  {
5060  Utilities->CallLogPop(716);
5061  return(false);
5062  }
5063  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5064  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5066  {
5067  Utilities->CallLogPop(717);
5068  return(false);
5069  }
5070  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5071 /*
5072  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5073  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5074  {
5075  Utilities->CallLogPop(718);
5076  return false;
5077  }
5078 */
5079  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5080  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5081  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5082  {
5083  if(StopRequired)
5084  {
5085  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5086  {
5087  if(!PlatformFoundFlag)
5088  {
5089  PlatformPosition = CurrentTrackVectorPosition;
5090  }
5091  // ensure this only set once at first valid platform position - the unrestricted route will end here
5092  PlatformFoundFlag = true;
5093  }
5094  }
5095  }
5096  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5097  // train has to be at station but that has to be before the next forward signal
5098 /*
5099  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5100  {
5101  Utilities->CallLogPop(719);
5102  return false;
5103  }
5104 */
5105  // make sure points are followed correctly (d) & set ExitPos
5106  if(CurrentTrackElement.TrackType == Points)
5107  {
5108  if((EntryPos == 0) || (EntryPos == 2))
5109  {
5110  if(CurrentTrackElement.Attribute == 0)
5111  {
5112  ExitPos = 1;
5113  }
5114  else
5115  {
5116  ExitPos = 3;
5117  }
5118  }
5119  if(EntryPos == 1)
5120  {
5121  if(CurrentTrackElement.Attribute == 0)
5122  {
5123  ExitPos = 0;
5124  }
5125  else
5126  {
5127  Utilities->CallLogPop(720);
5128  return(false);
5129  }
5130  }
5131  if(EntryPos == 3)
5132  {
5133  if(CurrentTrackElement.Attribute == 1)
5134  {
5135  ExitPos = 0;
5136  }
5137  else
5138  {
5139  Utilities->CallLogPop(721);
5140  return(false);
5141  }
5142  }
5143  }
5144  else
5145  {
5146  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5147  }
5148  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5149  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5150  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5151  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5152  if(ElementNumber < 2)
5153  {
5154  SkipRouteCheck = true;
5155  }
5156  else
5157  {
5158  SkipRouteCheck = false;
5159  }
5160  if(ElementNumber == 1) // the stop signal
5161  {
5162  RouteStartPosition = CurrentTrackVectorPosition;
5163  }
5164 /*
5165  if(ElementNumber == 2)
5166  {
5167  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5168  else AutoSigs = false;
5169  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5170  }
5171 */
5172  if(ElementNumber > 1)
5173  {
5174  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5175  {
5176  RouteOrPartRouteSet = true;
5177  }
5178  else
5179  {
5180  RouteOrPartRouteSet = false;
5181  }
5182  }
5183  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5184  {
5185  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5186  {
5187  Utilities->CallLogPop(1859);
5188  return(false);
5189  }
5190  int ExitLink = CurrentTrackElement.Link[ExitPos];
5191  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5192  {
5193  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5194  {
5195  Utilities->CallLogPop(1850);
5196  return(false);
5197  }
5198  }
5199  }
5200  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5201  if(EntryPos < 2)
5202  {
5203  Distance += CurrentTrackElement.Length01;
5204  }
5205  else
5206  {
5207  Distance += CurrentTrackElement.Length23;
5208  }
5209  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5210  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5211  CurrentTrackVectorPosition = NextTrackVectorPosition;
5212  EntryPos = NextEntryPos;
5213  ElementNumber++;
5214  } // while(true)
5215 
5216  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5217  // from the stop signal (note that it may be last in an autosigs route)
5218  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5219  // this in ClockTimer2)
5220 
5221  // now add elements to the CallonVector
5222  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5223 
5224  AllRoutes->CallonVector.push_back(CallonEntry);
5225  Utilities->CallLogPop(1860);
5226  return(true); // return false if fail to set route for any reason
5227 }
5228 
5229 // ---------------------------------------------------------------------------
5230 /*
5231  bool TTrain::TimetableFinished()
5232  {
5233  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5234  {
5235  return true;
5236  }
5237  return false;
5238  }
5239 */
5240 // ---------------------------------------------------------------------------
5241 
5242 AnsiString TTrain::GetTrainHeadCode(int Caller)
5243 
5244 {
5245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5246  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5247 
5248  Utilities->CallLogPop(1452);
5249  return(RepeatHeadCode);
5250 }
5251 
5252 // ---------------------------------------------------------------------------
5253 
5254 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5255 {
5256  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5257  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5258 
5259  Utilities->CallLogPop(1453);
5260  return(RepeatTime);
5261 }
5262 
5263 // ---------------------------------------------------------------------------
5264 
5265 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5266 {
5267  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5268  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5269  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5270  // first check that train is fully on the railway
5271  bool FrontValid = false, RearValid = false;
5272  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5273 
5274  if((LeadElement == -1) || (MidElement == -1))
5275  {
5276  TrainToBeJoinedBy = NULL;
5277  Utilities->CallLogPop(2131);
5278  return(false);
5279  }
5281  {
5282  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5283  FrontValid = true;
5284  }
5286  {
5287  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5288  RearValid = true;
5289  }
5290  int TrainToBeJoinedByID = -1;
5291 
5292  // first check if on a 2-track element & select correct ID number if so
5293  if(FrontValid)
5294  {
5295  if(FrontAdjacentTrackElement.TrackType == Bridge)
5296  {
5298  {
5299  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5300  }
5301  else
5302  {
5303  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5304  }
5305  }
5306  else
5307  {
5308  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5309  }
5310  }
5311  if((TrainToBeJoinedByID < 0) && RearValid)
5312  {
5313  // first check if on a 2-track element & select correct ID number if so
5314  if(RearAdjacentTrackElement.TrackType == Bridge)
5315  {
5317  {
5318  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5319  }
5320  else
5321  {
5322  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5323  }
5324  }
5325  else
5326  {
5327  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5328  }
5329  }
5330  if(TrainToBeJoinedByID < 0) // no adjacent train
5331  {
5332  TrainToBeJoinedBy = NULL;
5333  Utilities->CallLogPop(2132);
5334  return(false);
5335  }
5336  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5337  if(!TrainToBeJoinedBy->Stopped())
5338  {
5339  TrainToBeJoinedBy = NULL;
5340  Utilities->CallLogPop(2133);
5341  return(false);
5342  }
5343  Utilities->CallLogPop(2134);
5344  return(true);
5345 }
5346 
5347 // ---------------------------------------------------------------------------
5348 
5349 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution,
5350  TDateTime TimetableNonRepeatTime, bool Warning)
5351 /*
5352  Time = timetable time, the time adjustments for repeat trains is carried out internally
5353  Not all messages need this, if not needed a dummy value is required but not used
5354 
5355  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5356  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5357  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5358  //NB for Frh just give terminated message but without event time - don't use this function
5359  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5360  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5361  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5362  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5363  FrontSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from front to 3D54 at Old Street 1 minute late
5364  RearSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from rear to 3D54 at Old Street 1 minute late
5365  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5366  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5367  ChangeDescription: 06:05:40: 2F46 changed its description to 'NewDescription' at Old Street 1 minute late
5368  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5369  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5370  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5371  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5372  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5373  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5374  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5375  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5376  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5377  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5378  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5379  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5380  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5381  SignallerStop 06:05:40: 2F46 stopped on signaller command
5382  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5383  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5384 */{
5385  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5386  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5387  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5388  int IntMinsLate = 0;
5389 
5390  // need to set it in case MinsLate == 0, since it isn't tested for that
5391  if(ActionType == Arrive)
5392  {
5393  ActionLog = " arrived at ";
5394  }
5395  if(ActionType == Terminate)
5396  {
5397  if(TerminatedMessageSent) // to avoid it being sent twice
5398  {
5399  Utilities->CallLogPop(1104);
5400  return;
5401  }
5402  ActionLog = " terminated at ";
5403  TerminatedMessageSent = true;
5404  }
5405  if(ActionType == Depart)
5406  {
5407  ActionLog = " departed from ";
5408  }
5409  if(ActionType == Pass)
5410  {
5411  ActionLog = " passed ";
5412  }
5413  if(ActionType == Create)
5414  {
5415  ActionLog = " created at ";
5416  }
5417  if(ActionType == Enter)
5418  {
5419  ActionLog = " entered railway at ";
5420  }
5421  if(ActionType == ChangeDescription)
5422  {
5423  ActionLog = " changed its description to '" + Description + "' at "; //changed to train description at v2.16.1
5424  }
5425  if(ActionType == Leave)
5426  {
5427  ActionLog = " left railway at ";
5428  }
5429  if(ActionType == FrontSplit)
5430  {
5431  ActionLog = " split mass%-Power% = " + SplitDistribution + " from front to ";
5432  }
5433  if(ActionType == RearSplit)
5434  {
5435  ActionLog = " split mass%-Power% = " + SplitDistribution + " from rear to ";
5436  }
5437  if(ActionType == JoinedByOther)
5438  {
5439  ActionLog = " joined by ";
5440  }
5441  if(ActionType == ChangeDirection)
5442  {
5443  ActionLog = " changed direction at ";
5444  }
5445  if(ActionType == NewService)
5446  {
5447  ActionLog = " became new service ";
5448  }
5449  if(ActionType == TakeSignallerControl)
5450  {
5451  ActionLog = " taken under signaller control at ";
5452  }
5453  if(ActionType == RestoreTimetableControl)
5454  {
5455  ActionLog = " restored to timetable control at ";
5456  }
5457  if(ActionType == RemoveTrain)
5458  {
5459  if(Crashed)
5460  {
5461  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5462  }
5463  else if(Derailed)
5464  {
5465  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5466  }
5467  else
5468  {
5469  ActionLog = " REMOVED FROM RAILWAY at ";
5470  }
5471  }
5472  if(ActionType == SignallerMoveForwards)
5473  {
5474  ActionLog = " received signaller authority to proceed";
5475  }
5476  if(ActionType == SignallerStepForward)
5477  {
5478  ActionLog = " received signaller authority to step forward";
5479  }
5480  if(ActionType == SignallerChangeDirection)
5481  {
5482  ActionLog = " changed direction under signaller control at ";
5483  }
5484  if(ActionType == SignallerPassRedSignal)
5485  {
5486  ActionLog = " received signaller authority to pass stop signal";
5487  }
5488  if(ActionType == SignallerControlStop)
5489  {
5490  ActionLog = " received signaller instruction to stop";
5491  }
5492  if(ActionType == SignallerStop)
5493  {
5494  ActionLog = " stopped on signaller instruction ";
5495  }
5496  if(ActionType == SignallerJoin)
5497  {
5498  ActionLog = " joined under signaller control by ";
5499  }
5500  if(ActionType == TrainFailure)
5501  {
5502  ActionLog = " suffered an onboard power failure at ";
5503  }
5504  if(ActionType == RepairFailedTrain)
5505  {
5506  ActionLog = " failure repaired at ";
5507  }
5508  if(ActionType == SignallerLeave)
5509  {
5510  ActionLog = " left railway under signaller control at ";
5511  }
5512  if(OtherHeadCode != "")
5513  {
5514  OtherHeadCode += " at ";
5515  }
5516  TDateTime ActualTime = TrainController->TTClockTime;
5517 
5518  if(Warning)
5519  {
5520  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5521  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5522  }
5523  else
5524  {
5525  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5526  }
5527  bool TimePerformance = true;
5528 
5529  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5530  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5531  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5532  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5533  // SignallerJoin & RepairFailedTrain new at v2.4.0
5534  {
5535  TimePerformance = false;
5536  }
5537  if(TimePerformance)
5538  {
5539  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5540  MinsDelayed = float(MinsLate);
5541  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5542  {
5543  MinsDelayed = 0;
5544  }
5545  // new v2.2.0 for OpActionPanel, can be positive or negative
5546  if(ActionType == Arrive)
5547  {
5549  }
5550  // since train has just arrived this value is the
5551  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5552  // subtracted from later stop recoverable times.
5553  if(MinsLate < 0)
5554  {
5555  IntMinsLate = int(ceil(MinsLate));
5556  }
5557  if(MinsLate > 0)
5558  {
5559  IntMinsLate = int(floor(MinsLate));
5560  }
5561  if(IntMinsLate == 0)
5562  {
5563  PerfLog = " on time";
5564  }
5565  else if(IntMinsLate == 1)
5566  {
5567  PerfLog = " 1 minute late";
5568  }
5569  else if(IntMinsLate == -1)
5570  {
5571  PerfLog = " 1 minute early";
5572  }
5573  else if(IntMinsLate > 1)
5574  {
5575  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5576  }
5577  else if(IntMinsLate < -1)
5578  {
5579  int PosIntMinsLate = -IntMinsLate;
5580  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5581  }
5582  if(LocationName.Pos('-') > 0)
5583  {
5584  PerfLog = "," + PerfLog;
5585  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5586  }
5587  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5588  }
5589  else
5590  {
5591  PerfLogForm->PerformanceLog(1, BaseLog);
5592  }
5593  if(Warning)
5594  {
5595  Display->WarningLog(0, WarningBaseLog);
5596  }
5597  // update statistics
5598  if((ActionType == Arrive) && (IntMinsLate == 0))
5599  {
5601  }
5602  else if((ActionType == Arrive) && (IntMinsLate > 0))
5603  {
5605  TrainController->TotLateArrMins += IntMinsLate;
5606  }
5607  else if((ActionType == Arrive) && (IntMinsLate < 0))
5608  {
5610  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5611  }
5612 
5613  else if((ActionType == Pass) && (IntMinsLate == 0))
5614  {
5616  }
5617  else if((ActionType == Pass) && (IntMinsLate > 0))
5618  {
5620  TrainController->TotLatePassMins += IntMinsLate;
5621  }
5622  else if((ActionType == Pass) && (IntMinsLate < 0))
5623  {
5625  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5626  }
5627 
5628  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5629  {
5631  }
5632  else if((ActionType == Leave) && (IntMinsLate > 0))
5633  {
5635  TrainController->TotLateExitMins += IntMinsLate;
5636  }
5637  else if((ActionType == Leave) && (IntMinsLate < 0))
5638  {
5640  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5641  }
5642 
5643  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5644  {
5646  }
5647  else if((ActionType == Depart) && (IntMinsLate > 0))
5648  {
5650  TrainController->TotLateDepMins += IntMinsLate;
5651  }
5652  Utilities->CallLogPop(968);
5653 }
5654 
5655 // ---------------------------------------------------------------------------
5656 
5657 void TTrain::TrainHasFailed(int Caller)
5658 {
5659  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5660  if(Crashed || Derailed || DerailPending)
5661  {
5662  TrainFailurePending = false;
5663  Utilities->CallLogPop(2135);
5664  return;
5665  }
5666  AnsiString LocName = "";
5667 
5668  if(LeadElement > -1)
5669  {
5671  }
5672  if((LocName == "") && (MidElement > -1))
5673  {
5675  }
5676  if((LocName == "") && LeadElement > -1)
5677  {
5678  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5679  }
5680  if((LocName == "") && (MidElement > -1))
5681  {
5682  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5683  }
5684  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5685  TrainFailed = true;
5686  TrainFailurePending = false;
5687  CallingOnFlag = false; //added at v2.10.0
5689  PowerAtRail = 0.08;
5690  AValue = sqrt(2 * PowerAtRail / Mass);
5692  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5693  if(Stopped())
5694  {
5695  EntrySpeed = 0;
5696  ExitSpeedHalf = 0;
5697  ExitSpeedFull = 0;
5698  MaxExitSpeed = 0;
5699  BrakeRate = 0;
5700  StoppedWithoutPower = true;
5701  }
5703  LogAction(33, HeadCode, "", TrainFailure, LocName, "", TDateTime(0), true);
5704  // true for warning, TDateTime not used
5705  Utilities->CallLogPop(2136);
5706 }
5707 
5708 // ---------------------------------------------------------------------------
5709 
5710 void TTrain::FrontTrainSplit(int Caller)
5711 {
5712 /*
5713  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5714  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5715  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5716  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5717 */
5718  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5719  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5720  if(PowerAtRail < 1)
5721  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5722  {
5724  {
5725  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5726  }
5728  Utilities->CallLogPop(2137);
5729  return;
5730  }
5731  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5732 
5733  if(LocationName == "")
5734  {
5735  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5736  }
5737  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5738  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5739  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5741 
5742  // determine all positions & exits
5743  if(LocationName != "")
5744  {
5745  // if message given only call at ~5 sec intervals
5747  {
5748  FirstNamedElementPos = LeadElement;
5749  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5750  // check if possible with LeadElement as First
5751  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5752  {
5753  FirstNamedElementPos = MidElement;
5754  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5755  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5756  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5757  {
5759  {
5760  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5762  }
5763  Utilities->CallLogPop(1009);
5764  return;
5765  }
5766  }
5767  else
5768  {
5769  // if first is possible then check if all 4 positions at location, and if not try the second
5770  int LeadPosA = FirstNamedElementPos;
5771  int LeadPosB = FirstNamedLinkedElementPos;
5772  int LeadPosC = SecondNamedElementPos;
5773  int LeadPosD = SecondNamedLinkedElementPos;
5774  // count number of positions that are at the location
5775  int LeadNumAtLoc = 0;
5776  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5777  {
5778  LeadNumAtLoc++;
5779  }
5780  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5781  {
5782  LeadNumAtLoc++;
5783  }
5784  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5785  {
5786  LeadNumAtLoc++;
5787  }
5788  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5789  {
5790  LeadNumAtLoc++;
5791  }
5792  if(LeadNumAtLoc < 4)
5793  {
5794  FirstNamedElementPos = MidElement;
5795  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5796  SecondNamedLinkedElementPos)) // restore originals
5797  {
5798  FirstNamedElementPos = LeadPosA;
5799  FirstNamedLinkedElementPos = LeadPosB;
5800  SecondNamedElementPos = LeadPosC;
5801  SecondNamedLinkedElementPos = LeadPosD;
5802  }
5803  else // count number at location
5804  {
5805  int MidNumAtLoc = 0;
5806  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5807  {
5808  MidNumAtLoc++;
5809  }
5810  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5811  {
5812  MidNumAtLoc++;
5813  }
5814  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5815  {
5816  MidNumAtLoc++;
5817  }
5818  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5819  {
5820  MidNumAtLoc++;
5821  }
5822  if(LeadNumAtLoc > MidNumAtLoc)
5823  // change back, else keep new values
5824  {
5825  FirstNamedElementPos = LeadPosA;
5826  FirstNamedLinkedElementPos = LeadPosB;
5827  SecondNamedElementPos = LeadPosC;
5828  SecondNamedLinkedElementPos = LeadPosD;
5829  }
5830  }
5831  }
5832  }
5833  }
5834  else
5835  {
5836  Utilities->CallLogPop(1791);
5837  return;
5838  }
5839  }
5840  else
5841  {
5842  throw Exception("Error - LocationName not set in FrontTrainSplit");
5843  }
5844  // set front & rear train parameters
5845  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5846  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5847  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5848  if(LeadElement == FirstNamedElementPos)
5849  {
5850  if(MidElement == SecondNamedElementPos)
5851  {
5852  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5853  FrontTrainRearPosition = FirstNamedElementPos;
5854  RearTrainFrontPosition = SecondNamedElementPos;
5855  RearTrainRearPosition = SecondNamedLinkedElementPos;
5856  }
5857  else // MidElement must == FirstNamedLinkedElementPos
5858  {
5859  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5860  FrontTrainRearPosition = SecondNamedElementPos;
5861  RearTrainFrontPosition = FirstNamedElementPos;
5862  RearTrainRearPosition = FirstNamedLinkedElementPos;
5863  }
5864  }
5865  else // MidElement == FirstNamedElementPos
5866  {
5867  if(LeadElement == SecondNamedElementPos)
5868  {
5869  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5870  FrontTrainRearPosition = SecondNamedElementPos;
5871  RearTrainFrontPosition = FirstNamedElementPos;
5872  RearTrainRearPosition = FirstNamedLinkedElementPos;
5873  }
5874  else // LeadElement must == FirstNamedLinkedElementPos
5875  {
5876  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5877  FrontTrainRearPosition = FirstNamedElementPos;
5878  RearTrainFrontPosition = SecondNamedElementPos;
5879  RearTrainRearPosition = SecondNamedLinkedElementPos;
5880  }
5881  }
5882  RearTrainExitPos = -1;
5883  for(int x = 0; x < 4; x++)
5884  {
5885  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5886  {
5887  RearTrainExitPos = x;
5888  break;
5889  }
5890  }
5891  if(RearTrainExitPos == -1)
5892  {
5893  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5894  }
5895  FrontTrainExitPos = -1;
5896  for(int x = 0; x < 4; x++)
5897  {
5898  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5899  {
5900  FrontTrainExitPos = x;
5901  break;
5902  }
5903  }
5904  if(FrontTrainExitPos == -1)
5905  {
5906  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5907  }
5908  // check no train (apart from self) on any of the 4 elements & fail if so
5909  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5910  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5911 
5912  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5913  {
5914  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5915  }
5916  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5917  {
5918  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5919  }
5920  else
5921  {
5922  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5923  }
5924  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5925  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5926  // can't be a bridge
5927  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5928  // can't be a bridge
5929  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5930  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5931 
5932  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5933  {
5934  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5935  }
5936  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5937  {
5938  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5939  }
5940  else
5941  {
5942  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5943  }
5944  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5945  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5946  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5947  {
5949  {
5952  }
5953  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5954  Utilities->CallLogPop(1010);
5955  return;
5956  }
5958  {
5960  }
5961  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5962  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5963  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5964  // variable as it is needed for setting up the new train
5965  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5966  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
5967 
5968  UnplotTrain(0);
5969  StartSpeed = 0;
5970  RearStartElement = RearTrainRearPosition;
5971  RearStartExitPos = RearTrainExitPos;
5972  StoppedAtLocation = true;
5973  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5974  {
5975  StoppedWithoutPower = true;
5976  }
5977  PlotStartPosition(3);
5980 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
5982 
5983  //new at v2.15.0 for unequal split in mass & power
5984  int NewTrainMass;
5985  double NewTrainPowerAtRail;
5987  {
5988  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
5989  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
5990  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
5991  NewTrainMass = Mass * double(MassPercent)/100.0;
5992  Mass = Mass - NewTrainMass;
5993  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
5994  if(NewTrainPowerAtRail == 0)
5995  {
5996  NewTrainPowerAtRail = 0.08; //min value represents 0
5997  }
5998  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
5999  AValue = sqrt(2 * PowerAtRail / Mass);
6000  }
6001  else
6002  {
6003  Mass = Mass / 2;
6004  NewTrainMass = Mass;
6005  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6006  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6007  // and when needed it's calculated from rate & mass - changed at v2.15.0
6008  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6009  PowerAtRail = PowerAtRail / 2;
6010  NewTrainPowerAtRail = PowerAtRail;
6011  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6012  AValue = sqrt(2 * PowerAtRail / Mass);
6013  // shouldn't change but include in case not set earlier
6014  }
6015 
6016  ActionVectorEntryPtr++; //moved here at v2.15.0
6017  // create new front train
6018 /*
6019  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6020  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6021  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
6022 */
6023  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6024  TActionEventType EventType = NoEvent;
6025 
6026  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6027  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6028  // false for SignallerControl
6029  {
6030  Utilities->CallLogPop(1721); // EventType not used here
6031  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6032  // another train, in which case a message will have been sent to the perf log, also might well clear later
6033  // when other train moves away
6034  return;
6035  }
6036 
6037  TrainController->TrainVector.back().Description = OldActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
6038  if(!OldActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6039  {
6040 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
6041  TrainController->TrainVector.back().Description = OriginalDescription; //else takes it from this train's description
6042  }
6043 
6044  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6045  // see mods in UpdateTrain for v1.3.2
6046  TrainController->TrainAdded = true;
6047 
6048  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6049 
6050  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6051  TTOD.RunningEntry = Running;
6052  Utilities->CallLogPop(998);
6053 }
6054 
6055 // ---------------------------------------------------------------------------
6056 
6057 void TTrain::RearTrainSplit(int Caller)
6058 {
6059 /*
6060  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
6061  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
6062  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
6063  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
6064 */
6065  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6066  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6067  if(PowerAtRail < 1)
6068  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
6069  {
6071  {
6072  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
6073  }
6075  Utilities->CallLogPop(2138);
6076  return;
6077  }
6078  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
6079 
6080  if(LocationName == "")
6081  {
6082  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
6083  }
6084  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
6085  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
6086  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
6088 
6089  // determine all positions & exits
6090  if(LocationName != "")
6091  {
6092  // if message given only call at ~5 sec intervals
6094  {
6095  FirstNamedElementPos = LeadElement;
6096  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6097  SecondNamedLinkedElementPos))
6098  {
6099  FirstNamedElementPos = MidElement;
6100  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6101  SecondNamedLinkedElementPos))
6102  {
6104  {
6105  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
6107  }
6108  Utilities->CallLogPop(1013);
6109  return;
6110  }
6111  }
6112  else
6113  {
6114  // if first is possible then check if all 4 positions at location, and if not try the second
6115  int LeadPosA = FirstNamedElementPos;
6116  int LeadPosB = FirstNamedLinkedElementPos;
6117  int LeadPosC = SecondNamedElementPos;
6118  int LeadPosD = SecondNamedLinkedElementPos;
6119  // count number of positions that are at the location
6120  int LeadNumAtLoc = 0;
6121  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
6122  {
6123  LeadNumAtLoc++;
6124  }
6125  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
6126  {
6127  LeadNumAtLoc++;
6128  }
6129  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
6130  {
6131  LeadNumAtLoc++;
6132  }
6133  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
6134  {
6135  LeadNumAtLoc++;
6136  }
6137  if(LeadNumAtLoc < 4)
6138  {
6139  FirstNamedElementPos = MidElement;
6140  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6141  SecondNamedLinkedElementPos)) // restore originals
6142  {
6143  FirstNamedElementPos = LeadPosA;
6144  FirstNamedLinkedElementPos = LeadPosB;
6145  SecondNamedElementPos = LeadPosC;
6146  SecondNamedLinkedElementPos = LeadPosD;
6147  }
6148  else // count number at location
6149  {
6150  int MidNumAtLoc = 0;
6151  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
6152  {
6153  MidNumAtLoc++;
6154  }
6155  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6156  {
6157  MidNumAtLoc++;
6158  }
6159  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6160  {
6161  MidNumAtLoc++;
6162  }
6163  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6164  {
6165  MidNumAtLoc++;
6166  }
6167  if(LeadNumAtLoc > MidNumAtLoc)
6168  // change back, else keep new values
6169  {
6170  FirstNamedElementPos = LeadPosA;
6171  FirstNamedLinkedElementPos = LeadPosB;
6172  SecondNamedElementPos = LeadPosC;
6173  SecondNamedLinkedElementPos = LeadPosD;
6174  }
6175  }
6176  }
6177  }
6178  }
6179  else
6180  {
6181  Utilities->CallLogPop(1792);
6182  return;
6183  }
6184  }
6185  else
6186  {
6187  throw Exception("Error - LocationName not set in RearTrainSplit");
6188  }
6189  // set front & rear train parameters
6190  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
6191  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
6192  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
6193  if(LeadElement == FirstNamedElementPos)
6194  {
6195  if(MidElement == SecondNamedElementPos)
6196  {
6197  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6198  FrontTrainRearPosition = FirstNamedElementPos;
6199  RearTrainFrontPosition = SecondNamedElementPos;
6200  RearTrainRearPosition = SecondNamedLinkedElementPos;
6201  }
6202  else // MidElement must == FirstNamedLinkedElementPos
6203  {
6204  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6205  FrontTrainRearPosition = SecondNamedElementPos;
6206  RearTrainFrontPosition = FirstNamedElementPos;
6207  RearTrainRearPosition = FirstNamedLinkedElementPos;
6208  }
6209  }
6210  else // MidElement == FirstNamedElementPos
6211  {
6212  if(LeadElement == SecondNamedElementPos)
6213  {
6214  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6215  FrontTrainRearPosition = SecondNamedElementPos;
6216  RearTrainFrontPosition = FirstNamedElementPos;
6217  RearTrainRearPosition = FirstNamedLinkedElementPos;
6218  }
6219  else // LeadElement must == FirstNamedLinkedElementPos
6220  {
6221  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6222  FrontTrainRearPosition = FirstNamedElementPos;
6223  RearTrainFrontPosition = SecondNamedElementPos;
6224  RearTrainRearPosition = SecondNamedLinkedElementPos;
6225  }
6226  }
6227  RearTrainExitPos = -1;
6228  for(int x = 0; x < 4; x++)
6229  {
6230  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
6231  {
6232  RearTrainExitPos = x;
6233  break;
6234  }
6235  }
6236  if(RearTrainExitPos == -1)
6237  {
6238  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
6239  }
6240  FrontTrainExitPos = -1;
6241  for(int x = 0; x < 4; x++)
6242  {
6243  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6244  {
6245  FrontTrainExitPos = x;
6246  break;
6247  }
6248  }
6249  if(FrontTrainExitPos == -1)
6250  {
6251  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6252  }
6253  // check no train (apart from self) on any of the 4 elements & fail if so
6254  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6255  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6256 
6257  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6258  {
6259  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6260  }
6261  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6262  {
6263  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6264  }
6265  else
6266  {
6267  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6268  }
6269  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6270  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6271  // can't be a bridge
6272  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6273  // can't be a bridge
6274  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6275  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6276 
6277  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6278  {
6279  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6280  }
6281  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6282  {
6283  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6284  }
6285  else
6286  {
6287  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6288  }
6289  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6290  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6291  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6292  {
6294  {
6297  }
6298  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6299  Utilities->CallLogPop(1014);
6300  return;
6301  }
6303  {
6305  }
6306  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6307  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6308  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6309  // variable as it is needed for setting up the new train
6310  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6311  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6312  UnplotTrain(1);
6313  StartSpeed = 0;
6314  RearStartElement = FrontTrainRearPosition;
6315  RearStartExitPos = FrontTrainExitPos;
6316  StoppedAtLocation = true;
6317  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6318  {
6319  StoppedWithoutPower = true;
6320  }
6321  PlotStartPosition(4);
6324 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
6326 
6327  //new at v2.15.0 for unequal split in mass & power
6328  int NewTrainMass;
6329  double NewTrainPowerAtRail;
6331  {
6332  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
6333  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
6334  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
6335  NewTrainMass = Mass * double(MassPercent)/100.0;
6336  Mass = Mass - NewTrainMass;
6337  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
6338  if(NewTrainPowerAtRail == 0)
6339  {
6340  NewTrainPowerAtRail = 0.08; //min value represents 0
6341  }
6342  PowerAtRail = PowerAtRail - NewTrainPowerAtRail ;
6343  AValue = sqrt(2 * PowerAtRail / Mass);
6344  }
6345  else
6346  {
6347  Mass = Mass / 2;
6348  NewTrainMass = Mass;
6349  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6350  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6351  // and when needed it's calculated from rate & mass - changed at v2.15.0
6352  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6353  PowerAtRail = PowerAtRail / 2;
6354  NewTrainPowerAtRail = PowerAtRail;
6355  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6356  AValue = sqrt(2 * PowerAtRail / Mass);
6357  // shouldn't change but include in case not set earlier
6358  }
6359 
6360  ActionVectorEntryPtr++; //moved here at v2.15.0
6361 
6362  // create new rear train
6363 /*
6364  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6365  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6366  int RepeatNumber, int IncrementalMinutes)
6367 */
6368  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6369  TActionEventType EventType = NoEvent;
6370 
6371  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6372  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6373  // false for SignallerControl
6374  {
6375  Utilities->CallLogPop(1722); // EventType not used here
6376  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6377  // another train, in which case a message will have been sent to the perf log, also might well clear later
6378  // when other train moves away
6379  return;
6380  }
6381 
6382  TrainController->TrainVector.back().Description = OldActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
6383  if(!OldActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6384  {
6385 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
6386  TrainController->TrainVector.back().Description = OriginalDescription; //else takes it from this train's description
6387  }
6388 
6389  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6390  // see mods in UpdateTrain for v1.3.2
6391  TrainController->TrainAdded = true;
6392  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6393 
6394  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6395  TTOD.RunningEntry = Running;
6396  Utilities->CallLogPop(1015);
6397 }
6398 
6399 // ---------------------------------------------------------------------------
6400 
6401 void TTrain::FinishJoin(int Caller)
6402 {
6403  if(FinishJoinLogSent == false)
6404  {
6405  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6406  FinishJoinLogSent = true; // so don't keep logging this event
6407  // don't need to reset it to false after the event as the train is deleted
6408  }
6409  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6410  if(TrainFailed)
6411  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6412  {
6414  {
6415  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6416  }
6418  Utilities->CallLogPop(2139);
6419  return;
6420  }
6421  if(TrainGone)
6422  // this means that the train has already joined the other & is awaiting deletion by TrainController
6423  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6424  // on from jbo & TrainToJoinIsAdjacent returns false
6425  {
6426  Utilities->CallLogPop(1035);
6427  return;
6428  }
6429  TTrain *TrainToJoin;
6431 
6432  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6433  {
6435  {
6436  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6439  }
6440  Utilities->CallLogPop(1030);
6441  return; // keep this here in case need to add code before final return
6442  }
6443  // no need to clear error report flag here, cleared in jbo function
6444  // No need to set TimetableFinished, done in jbo function
6445  Utilities->CallLogPop(1031);
6446 }
6447 
6448 // ---------------------------------------------------------------------------
6449 
6450 void TTrain::JoinedBy(int Caller)
6451 {
6452  if(TrainController->OpTimeToActUpdateCounter == 0) //added at v2.13.2. Use OpTimeToActUpdateCounter for convenience so only issue the event log
6453  //once every second rather than many times. Can't use an event logged flag because there may
6454  //be several trains that are to be joined by others
6455  {
6456  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Waiting to be joined");
6457  }
6458  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6459  if(PowerAtRail < 1)
6460  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6461  {
6463  {
6464  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6465  }
6467  Utilities->CallLogPop(2140);
6468  return;
6469  }
6470  TTrain *TrainToBeJoinedBy;
6472 
6473  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6474  {
6476  {
6477  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6480  }
6481  LastActionDelayFlag = true;
6482  // need to update LastActionTime if this train first to arrive as need 30s after
6483  // both adjacent before the join
6484  Utilities->CallLogPop(1032);
6485  return;
6486  }
6487  // here when other train is adjacent
6489  {
6491  // need to update this as need 30s after both adjacent before the join
6492  LastActionDelayFlag = false;
6493  Utilities->CallLogPop(1033);
6494  return;
6495  }
6496  // here when other train is adjacent & 30 secs elapsed since both adjacent
6497 
6498  // set new values for mass etc
6499  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6500  {
6501  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6502  }
6503  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6504  double OwnBrakeForce = MaxBrakeRate * Mass;
6505  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6506 
6507  Mass += TrainToBeJoinedBy->Mass;
6508  MaxBrakeRate = CombinedBrakeRate;
6509  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6510  AValue = sqrt(2 * PowerAtRail / Mass);
6511 
6513  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6514  TrainToBeJoinedBy->TimetableFinished = true;
6515  TrainToBeJoinedBy->TrainGone = true;
6516  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Joined By," + FJOHeadCode); //added at v2.13.2 to provide more information
6517  // this will cause other train to be deleted
6518  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6522  Utilities->CallLogPop(1034);
6523 }
6524 
6525 // ---------------------------------------------------------------------------
6526 
6527 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6528 {
6529  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6530  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6531  if(PowerAtRail < 1)
6532  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6533  {
6535  {
6536  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6537  }
6538  ZeroPowerNoCDTMessage = true;
6539  Utilities->CallLogPop(2141);
6540  return;
6541  }
6542  TColor TempColour = BackgroundColour;
6543 
6544  UnplotTrain(2);
6547  StartSpeed = 0;
6548  StoppedAtLocation = true;
6549  PlotStartPosition(1);
6550  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6551  // plot same as was - should always be pale green
6552  if(!NoLogFlag)
6553  {
6556  }
6558 
6559  //now erase a stub route if there is one, added at v2.5.1
6560  //first element of route is now immediately behind the train (i.e. next to MidElement)
6561  if(MidEntryPos >= 0)
6562  {
6563  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6564  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6565  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6566  int RouteNumber = -1;
6567  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6568  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6569  {
6570  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6571  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6572  //elements can continue to be removed from that route
6573  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6574 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6575  { //above condition removed v2.17.0 so non-facing signal or continuation doesn't stop route being removed
6576  //if it is a facing signal then it will be detected below and not removed
6577  bool FirstPass = true; //added at v2.8.0
6578  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6579  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6580  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6581  int TVPos2 = PDE.GetTrackVectorPosition();
6582  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6583  {
6584  break;
6585  }
6586  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6588  {
6589  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6590  }
6591  else
6592  {
6593  break;
6594  }
6595  FirstPass = false;
6596  }
6597  AllRoutes->RebuildRailwayFlag = true;
6598  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6599  }
6600  }
6601  }
6602  StoppedForTrainInFront = false;
6603  Utilities->CallLogPop(1012);
6604 }
6605 
6606 // ---------------------------------------------------------------------------
6607 
6608 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6609 // change to new train, give new service message
6610 //same RepeatNumber used for the new service
6611 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6612  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6613  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6614  if(PowerAtRail < 1)
6615  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6616  {
6618  {
6619  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6620  }
6622  Utilities->CallLogPop(2142);
6623  return;
6624  }
6626 
6627  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6628 
6629  if(!NoLogFlag)
6630  {
6632  }
6633  UnplotTrain(3);
6636  StartSpeed = 0;
6641  HeadCode = NewHeadCode;
6643  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6644  {
6645  Description = OriginalDescription; //changed at v2.16.1 to train description
6646  }
6647  StoppedAtLocation = true;
6648  PlotStartPosition(5);
6650  // pale green
6653  TerminatedMessageSent = false;
6654  Utilities->CallLogPop(1022);
6655 }
6656 
6657 // ---------------------------------------------------------------------------
6658 
6659 void TTrain::RemainHere(int Caller)
6660 {
6661  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6662  if(RemainHereLogNotSent) // to prevent repeated logs
6663  {
6664  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6665  RemainHereLogNotSent = false;
6666  }
6668  {
6672  TerminatedMessageSent = true;
6673  }
6674  TimetableFinished = true;
6675  Utilities->CallLogPop(1023);
6676 }
6677 
6678 // ---------------------------------------------------------------------------
6679 
6680 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6681 /*
6682  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6683  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6684  except where an action is a departure, starting at the current value for the pointer
6685  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6686  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6687 */{
6688  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6689  {
6690  return; // if remove train that starts under signaller control no messages needed
6691 
6692  }
6693  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6695  if(IncNum > 0)
6696  {
6697  for(int x = 0; x < IncNum; x++)
6698  {
6699  if(x > 0)
6700  {
6701  Ptr++;
6702  }
6703  // arrival - no need to test for termination as this section only covers missed actions up to the
6704  // arrival point - may terminate later but that not missed
6705  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6706  {
6708  }
6709  // arrival & departure
6710  if(Ptr->FormatType == TimeTimeLoc)
6711  {
6713  }
6714  // departure
6715  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6716  {
6717  continue; // skip TimeLoc departures, message given for arrivals
6718  }
6719  // pass
6720  else if(Ptr->FormatType == PassTime)
6721  {
6723  }
6724  // split
6725  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6726  {
6728  }
6729  // jbo
6730  else if(Ptr->Command == "jbo")
6731  {
6733  }
6734  // dsc
6735  else if(Ptr->Command == "dsc") //new at v2.15.0
6736  {
6738  }
6739  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6740  // be starts, finishes or cdt
6741  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6742  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6743  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6744  (Ptr->FormatType == Repeat))
6745  {
6746  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6747  }
6748  }
6749  }
6750  else
6751  {
6752  bool IncludeFER = false;
6753  if(IncNum == -1)
6754  {
6755  IncludeFER = true;
6756  }
6757  while(true) // finish commands & repeats break out of loop
6758  {
6759  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6760  if(!IncludeFER && (Ptr->Command == "Fer"))
6761  {
6762  break;
6763  }
6764  // Fer & included
6765  else if(IncludeFER && (Ptr->Command == "Fer"))
6766  {
6768  break;
6769  }
6770  // Repeat
6771  else if(Ptr->FormatType == Repeat)
6772  {
6773  break;
6774  }
6775  // Fjo
6776  else if(Ptr->Command == "Fjo")
6777  {
6779  break;
6780  }
6781  // Frh
6782  else if(Ptr->Command == "Frh")
6783  {
6785  {
6787  TerminatedMessageSent = true;
6788  }
6789  break;
6790  }
6791  // Frh-sh
6792  else if(Ptr->Command == "Frh-sh")
6793  {
6795  {
6797  TerminatedMessageSent = true;
6798  }
6799  break;
6800  }
6801  // Fns, F-nshs, Fns-sh
6802  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6803  {
6805  break;
6806  }
6807  // end of breakout actions
6808  // arrival
6809  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6810  {
6811  if(IsTrainTerminating(1))
6812  {
6814  TerminatedMessageSent = true;
6815  }
6816  else
6817  {
6819  }
6820  }
6821  // arrival & departure
6822  else if(Ptr->FormatType == TimeTimeLoc)
6823  {
6825  }
6826  // departure
6827  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6828  {
6829  Ptr++;
6830  continue; // skip TimeLoc departures, message given for arrivals
6831  }
6832  // pass
6833  else if(Ptr->FormatType == PassTime)
6834  {
6836  }
6837  // split
6838  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6839  {
6841  }
6842  // jbo
6843  else if(Ptr->Command == "jbo")
6844  {
6846  }
6847  // dsc
6848  else if(Ptr->Command == "dsc") //new at v2.15.0
6849  {
6850 // TrainController->LogActionError(65, HeadCode, "", FailMissedDSC, Ptr->LocationName); don't count as a missed event
6851  }
6852  // cdt
6853  else if(Ptr->Command == "cdt")
6854  {
6855 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6856  }
6857  // Errors
6858  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6859  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6860  {
6861  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6862  }
6863  Ptr++;
6864  }
6865  TimetableFinished = true;
6866  }
6867  Utilities->CallLogPop(1021);
6868 }
6869 
6870 // ---------------------------------------------------------------------------
6871 
6872 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6873 // ensure same repeatnumber
6874 {
6875  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6877 
6878  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6879  {
6880  Utilities->CallLogPop(1024);
6881  return(false);
6882  }
6883  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6884  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6885  {
6886  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6887  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6888  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6889  {
6890  Utilities->CallLogPop(1025);
6891  return(true);
6892  }
6893  }
6894  Utilities->CallLogPop(1026);
6895  return(false);
6896 }
6897 
6898 // ---------------------------------------------------------------------------
6899 
6900 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6901 // ensure same repeatnumber
6902 {
6903  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6904  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6905 
6906  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6907  {
6908  Utilities->CallLogPop(1027);
6909  return(false);
6910  }
6911  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6912  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6913  {
6914  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6915  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6916  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6917  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6918  {
6919  Utilities->CallLogPop(1028);
6920  return(true);
6921  }
6922  }
6923  Utilities->CallLogPop(1029);
6924  return(false);
6925 }
6926 
6927 // ---------------------------------------------------------------------------
6928 
6929 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6930 { //same RepeatNumber (i.e. 0) used for the new shuttle
6931 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6932  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6933  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6934  if(PowerAtRail < 1)
6935  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6936  {
6938  {
6939  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6940  }
6942  Utilities->CallLogPop(2143);
6943  return;
6944  }
6945  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6946  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6947 
6948  if(!NoLogFlag)
6949  {
6951  }
6952  UnplotTrain(4);
6955  StartSpeed = 0;
6960  HeadCode = NewHeadCode;
6962  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6963  {
6964  Description = OriginalDescription; //changed at v2.16.1 to train description
6965  }
6966  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6967  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6968  StoppedAtLocation = true;
6969  PlotStartPosition(6);
6971  // pale green
6974  TerminatedMessageSent = false;
6975  Utilities->CallLogPop(1078);
6976 }
6977 
6978 // ---------------------------------------------------------------------------
6979 
6980 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6981 // need to check whether all repeats finished or not
6982 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6983  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6984  if(RemainHereLogNotSent) // to prevent repeated logs
6985  {
6986  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6987  RemainHereLogNotSent = false;
6988  }
6990  // finished all repeats
6991  {
6993  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6996  TerminatedMessageSent = true;
6997  // no need to clear message as no more actions
6998  }
6999  TimetableFinished = true;
7000  Utilities->CallLogPop(1080);
7001  return;
7002  }
7003  if(PowerAtRail < 1)
7004  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
7005  {
7007  {
7008  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
7009  }
7011  Utilities->CallLogPop(2144);
7012  return;
7013  }
7014  int TempRepeatNumber = RepeatNumber + 1;
7015  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
7016  // until after LogAction or the wrong time will be used
7017  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
7018  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
7019 
7020  if(!NoLogFlag)
7021  {
7023  }
7024  RepeatNumber++;
7025  UnplotTrain(5);
7028  StartSpeed = 0;
7033  HeadCode = NewHeadCode;
7035  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
7036  {
7037  Description = OriginalDescription; //changed at v2.16.1 to train description
7038  }
7039  StoppedAtLocation = true;
7040  PlotStartPosition(7);
7042  // pale green
7045  TerminatedMessageSent = false;
7046  Utilities->CallLogPop(1079);
7047 }
7048 
7049 // ---------------------------------------------------------------------------
7050 
7051 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
7052 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
7053  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
7054  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
7055  if(PowerAtRail < 1)
7056  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
7057  {
7059  {
7060  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
7061  }
7063  Utilities->CallLogPop(2145);
7064  return;
7065  }
7067  // finished all repeats
7068  {
7069  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
7070  if(!NoLogFlag)
7071  {
7073  }
7074  RepeatNumber = 0;
7075  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
7076  UnplotTrain(6);
7079  StartSpeed = 0;
7081  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
7082  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
7084  HeadCode = NewHeadCode;
7086  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
7087  {
7088  Description = OriginalDescription; //changed at v2.16.1 to train description
7089  }
7090  StoppedAtLocation = true;
7091  PlotStartPosition(9);
7095  TerminatedMessageSent = false;
7096  Utilities->CallLogPop(1081);
7097  return;
7098  }
7099  int TempRepeatNumber = RepeatNumber + 1;
7100  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
7101  // until after LogAction or the wrong time will be used
7102  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
7103  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
7104 
7105  if(!NoLogFlag)
7106  {
7108  }
7109  RepeatNumber++;
7110  UnplotTrain(7);
7113  StartSpeed = 0;
7118  HeadCode = NewHeadCode;
7120  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
7121  {
7122  Description = OriginalDescription; //changed at v2.16.1 to train description
7123  }
7124  StoppedAtLocation = true;
7125  PlotStartPosition(8);
7127  // pale green
7130  TerminatedMessageSent = false;
7131  Utilities->CallLogPop(1082);
7132 }
7133 
7134 // ---------------------------------------------------------------------------
7135 
7137 {
7138  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
7139  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
7140  // must be preceded by a TimeLoc departure
7141  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
7142  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
7143  {
7145  {
7146  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
7147  {
7148  Utilities->CallLogPop(1083);
7149  return(false);
7150  }
7151  else if((ActionVectorEntryPtr + x)->SequenceType == FinishSequence)
7152  {
7153  Utilities->CallLogPop(1084);
7154  return(true);
7155  }
7156  }
7157  }
7158  Utilities->CallLogPop(1085);
7159  return(false);
7160 }
7161 
7162 // ---------------------------------------------------------------------------
7163 
7164 bool TTrain::AbleToMove(int Caller)
7165 {
7166  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
7167  bool Able = true;
7168 
7169  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
7170  {
7171  // StoppedForTrainInFront removed as tested below
7172  Utilities->CallLogPop(2146); // added v2.4.0
7173  return(false); // added v2.4.0
7174  }
7175  if(LeadElement > -1)
7176  {
7177  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
7178  {
7179  StoppedForTrainInFront = false;
7180  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
7181  Utilities->CallLogPop(2456);
7182  return(false);
7183  }
7184  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
7185  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
7186  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
7187  {
7188  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
7189  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
7190  {
7191  Able = true;
7192  StoppedForTrainInFront = false;
7193  }
7194  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
7195  {
7196  Able = true;
7197  StoppedForTrainInFront = false;
7198  }
7199  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
7200  {
7201  Able = true;
7202  StoppedForTrainInFront = false;
7203  }
7204  else
7205  {
7206  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
7207  }
7208  }
7209  }
7210  else // leaving at a continuation so keep going
7211  {
7212  Able = true;
7213  StoppedForTrainInFront = false;
7214  }
7215  Utilities->CallLogPop(1454);
7216  return(Able);
7217 }
7218 
7219 // ---------------------------------------------------------------------------
7220 
7222 {
7223  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
7224  // won't be set; if there is a train then set StoppedForTrainInFront
7225  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
7226  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
7227  if(LeadElement == -1) // exiting at continuation
7228  {
7229  Utilities->CallLogPop(2045);
7230  return(false);
7231  }
7232  // end of addition
7233  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
7234  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
7235 
7236  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
7237  {
7238  StoppedForTrainInFront = true;
7239  Utilities->CallLogPop(1455);
7240  return(false);
7241  }
7242  else
7243  {
7244  Utilities->CallLogPop(1456);
7246  // StoppedWithoutPower added v2.4.0
7247  }
7248 }
7249 
7250 // ---------------------------------------------------------------------------
7251 
7253 {
7254  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
7255  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
7256  TColor TempColour = BackgroundColour;
7257 
7258  UnplotTrain(8);
7261  StartSpeed = 0;
7262  PlotStartPosition(2);
7263  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7264 
7265  //now erase a stub route if there is one, added at v2.5.1
7266  //first element of route is now immediately behind the train (i.e. next to MidElement)
7267  if(MidEntryPos >= 0)
7268  {
7269  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7270  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7271  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7272  int RouteNumber = -1;
7273  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7274  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7275  {
7276  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7277  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7278  //elements can continue to be removed from that route
7279  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7280 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7281  { //above condition removed at v2.17.0 so non-facing signal or continuation doesn't stop route being removed
7282  //if it is a facing signal then it will be detected below and not removed
7283  bool FirstPass = true; //added at v2.8.0
7284  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7285  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7286  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7287  int TVPos2 = PDE.GetTrackVectorPosition();
7288  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7289  {
7290  break;
7291  }
7292  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7294  {
7295  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7296  }
7297  else
7298  {
7299  break;
7300  }
7301  FirstPass = false;
7302  }
7303  AllRoutes->RebuildRailwayFlag = true;
7304  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7305  }
7306  }
7307  }
7308  Utilities->CallLogPop(1102);
7309 }
7310 
7311 // ---------------------------------------------------------------------------
7312 
7314 {
7315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7316  ",FloatingLabelNextString" + "," + HeadCode);
7317  AnsiString RetStr = "", LocationName = "";
7318  //record action time - may be arrival, departure or event for use later (added at v2.13.2)
7319  TDateTime ActionTime = Ptr->ArrivalTime;
7320  if(ActionTime == TDateTime(-1))
7321  {
7322  ActionTime = Ptr->DepartureTime;
7323  }
7324  if(ActionTime == TDateTime(-1))
7325  {
7326  ActionTime = Ptr->EventTime;
7327  }
7328  //If ActionTime still TDateTime(-1) then the train has terminated and 'None...' will be returned
7329  //Now correct it for repeats
7330  if(ActionTime != TDateTime(-1))
7331  {
7332  ActionTime = GetTrainTime(64, ActionTime);
7333  }
7334  if(int(DelayedRandMins) > 0)
7335  {
7336  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7337  {
7338  throw Exception("Error - start entry in FloatingLabelNextString");
7339  }
7340  if(Ptr->FormatType == TimeTimeLoc)
7341  {
7342  if(TrainMode == Timetable)
7343  {
7344  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7345  // not arrived yet in tt mode
7346  {
7347  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7348  }
7349  else
7350  {
7351  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7352  }
7353  }
7354  else // TrainMode == Signaller
7355  {
7356  if(!DepartureTimeSet) // not arrived yet
7357  {
7358  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7359  }
7360  else
7361  {
7362  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7363  }
7364  }
7365  }
7366  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7367  {
7368  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7369  }
7370  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7371  {
7372  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7373  }
7374  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7375  {
7376  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7377  }
7378  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7379  {
7380  RetStr = "Pass " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7381  }
7382  else if(Ptr->Command == "Fns")
7383  {
7384  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7385  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7386  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7387  }
7388  else if(Ptr->Command == "F-nshs")
7389  {
7390  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7391  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7392  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7393  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7394  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7395  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7396  }
7397  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7398  {
7399  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7400  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7401  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7402  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7403  }
7404  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7405  {
7406  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7407  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7408  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7409  }
7410  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7411  {
7412  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7413  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7414  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7415  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7416  }
7417  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7418  {
7419  RetStr ="None, train terminated at " + Ptr->LocationName;
7420  }
7421  else if(Ptr->Command == "Frh")
7422  {
7423  RetStr = "None, train terminated at " + Ptr->LocationName;
7424  }
7425  else if(Ptr->Command == "Fer")
7426  {
7427  AnsiString AllowedExits = "";
7428  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7429  }
7430  else if(Ptr->Command == "Fjo")
7431  {
7432  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at approx. " +
7433  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7434  }
7435  else if(Ptr->Command == "jbo")
7436  {
7437  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7438  " at approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7439  }
7440  else if(Ptr->Command == "fsp")
7441  {
7442  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7443  " at approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7444  }
7445  else if(Ptr->Command == "rsp")
7446  {
7447  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7448  " at approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7449  }
7450  else if(Ptr->Command == "cdt")
7451  {
7452  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7453  }
7454  else if(Ptr->Command == "dsc")
7455  {
7456  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(65, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7457  }
7458  }
7459  else if(TrainController->TTClockTime > ActionTime) //condition added at v2.13.2 for trains that are delayed other than suffering a random delay
7460  {
7461  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7462  {
7463  throw Exception("Error - start entry in FloatingLabelNextString where TTClockTime > ActionTime");
7464  }
7465  if(Ptr->FormatType == TimeTimeLoc)
7466  {
7467  if(TrainMode == Timetable)
7468  {
7469  if(!TrainAtLocation(4, LocationName) || (LocationName != Ptr->LocationName))
7470  // not arrived yet in tt mode
7471  {
7472  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7473  }
7474  else
7475  {
7476  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7477  }
7478  }
7479  else // TrainMode == Signaller
7480  {
7481  if(!DepartureTimeSet) // not arrived yet
7482  {
7483  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7484  }
7485  else
7486  {
7487  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7488  }
7489  }
7490  }
7491  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7492  {
7493  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7494  }
7495  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7496  {
7497  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7498  }
7499  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7500  {
7501  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7502  }
7503  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7504  {
7505  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7506  }
7507  else if(Ptr->Command == "Fns")
7508  {
7509  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(60, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7510  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7511  RetStr = GetNewServiceDepartureInfo(19, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7512  }
7513  else if(Ptr->Command == "F-nshs")
7514  {
7515  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7517  RetStr = GetNewServiceDepartureInfo(20, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7518  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7519  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7520  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7521  }
7522  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7523  {
7524  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(61, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7525  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7526  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7527  RetStr = GetNewServiceDepartureInfo(21, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7528  }
7529  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7530  {
7531  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7532  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7533  RetStr = GetNewServiceDepartureInfo(22, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7534  }
7535  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7536  {
7537  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(62, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7538  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7539  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7540  RetStr = GetNewServiceDepartureInfo(23, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7541  }
7542  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7543  {
7544  RetStr ="None, train terminated at " + Ptr->LocationName;
7545  }
7546  else if(Ptr->Command == "Frh")
7547  {
7548  RetStr = "None, train terminated at " + Ptr->LocationName;
7549  }
7550  else if(Ptr->Command == "Fer")
7551  {
7552  AnsiString AllowedExits = "";
7553  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(5, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime)*/ + AllowedExits;
7554  }
7555  else if(Ptr->Command == "Fjo")
7556  {
7557  RetStr = "Join " + TrainController->GetRepeatHeadCode(63, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7558 // Utilities->Format96HHMM(TrainController->TTClockTime);
7559  }
7560  else if(Ptr->Command == "jbo")
7561  {
7562  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(64, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7563 // " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7564  }
7565  else if(Ptr->Command == "fsp")
7566  {
7567  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(65, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7568  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7569  }
7570  else if(Ptr->Command == "rsp")
7571  {
7572  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(66, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7573  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7574  }
7575  else if(Ptr->Command == "cdt")
7576  {
7577  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7578  }
7579  else if(Ptr->Command == "dsc")
7580  {
7581  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7582  }
7583  }
7584  else //train not delayed
7585  {
7586  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7587  {
7588  throw Exception("Error - start entry in FloatingLabelNextString in final 'else'");
7589  }
7590  if(Ptr->FormatType == TimeTimeLoc)
7591  {
7592  if(TrainMode == Timetable)
7593  {
7594  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7595  // not arrived yet in tt mode
7596  {
7597  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7598  }
7599  else
7600  {
7601  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7602  }
7603  }
7604  else // TrainMode == Signaller
7605  {
7606  if(!DepartureTimeSet) // not arrived yet
7607  {
7608  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7609  }
7610  else
7611  {
7612  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7613  }
7614  }
7615  }
7616  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7617  {
7618  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7619  }
7620  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7621  {
7622  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7623  }
7624  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7625  {
7626  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7627  }
7628  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7629  {
7630  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7631  }
7632  else if(Ptr->Command == "Fns")
7633  {
7634  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7635  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7636  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7637  }
7638  else if(Ptr->Command == "F-nshs")
7639  {
7640  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7642  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7643  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7644  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7645  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7646  }
7647  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7648  {
7649  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7650  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7651  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7652  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7653  }
7654  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7655  {
7656  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7657  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7658  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7659  }
7660  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7661  {
7662  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7663  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7664  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7665  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7666  }
7667  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7668  {
7669  RetStr ="None, train terminated at " + Ptr->LocationName;
7670  }
7671  else if(Ptr->Command == "Frh")
7672  {
7673  RetStr = "None, train terminated at " + Ptr->LocationName;
7674  }
7675  else if(Ptr->Command == "Fer")
7676  {
7677  AnsiString AllowedExits = "";
7678  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime))*/ + AllowedExits;
7679  }
7680  else if(Ptr->Command == "Fjo")
7681  {
7682  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7683 // Utilities->Format96HHMM(GetTrainTime(58, Ptr->EventTime));
7684  }
7685  else if(Ptr->Command == "jbo")
7686  {
7687  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7688 // " at approx. " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7689  }
7690  else if(Ptr->Command == "fsp")
7691  {
7692  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7693  " at approx. " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7694  }
7695  else if(Ptr->Command == "rsp")
7696  {
7697  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7698  " at approx. " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7699  }
7700  else if(Ptr->Command == "cdt")
7701  {
7702  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7703  }
7704  else if(Ptr->Command == "dsc")
7705  {
7706  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(66, Ptr->EventTime));
7707  }
7708  }
7709  Utilities->CallLogPop(1124);
7710  return(RetStr);
7711 }
7712 
7713 // ---------------------------------------------------------------------------
7714 /* as was
7715 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7716 {
7717  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7718  ",FloatingLabelNextString" + "," + HeadCode);
7719  AnsiString RetStr = "", LocationName = "";
7720 
7721  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7722  {
7723  throw Exception("Error - start entry in FloatingLabelNextString");
7724  }
7725  if(Ptr->FormatType == TimeTimeLoc)
7726  {
7727  if(TrainMode == Timetable)
7728  {
7729  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7730  // not arrived yet in tt mode
7731  {
7732  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7733  }
7734  else
7735  {
7736  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7737  }
7738  }
7739  else // TrainMode == Signaller
7740  {
7741  if(!DepartureTimeSet) // not arrived yet
7742  {
7743  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7744  }
7745  else
7746  {
7747  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7748  }
7749  }
7750  }
7751  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7752  {
7753  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7754  }
7755  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7756  {
7757  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7758  }
7759  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7760  {
7761  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7762  }
7763  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7764  {
7765  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7766  }
7767  else if(Ptr->Command == "Fns")
7768  {
7769  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7770  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7771  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7772  }
7773  else if(Ptr->Command == "F-nshs")
7774  {
7775  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7776  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7777  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7778  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7779  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7780  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7781  }
7782  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7783  {
7784  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7785  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7786  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7787  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7788  }
7789  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7790  {
7791  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7792  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7793  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7794  }
7795  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7796  {
7797  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7798  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7799  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7800  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7801  }
7802  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7803  {
7804  RetStr ="None, train terminated at " + Ptr->LocationName;
7805  }
7806  else if(Ptr->Command == "Frh")
7807  {
7808  RetStr = "None, train terminated at " + Ptr->LocationName;
7809  }
7810  else if(Ptr->Command == "Fer")
7811  {
7812  AnsiString AllowedExits = "";
7813  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7814  }
7815  else if(Ptr->Command == "Fjo")
7816  {
7817  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7818  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7819  }
7820  else if(Ptr->Command == "jbo")
7821  {
7822  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7823  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7824  }
7825  else if(Ptr->Command == "fsp")
7826  {
7827  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7828  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7829  }
7830  else if(Ptr->Command == "rsp")
7831  {
7832  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7833  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7834  }
7835  else if(Ptr->Command == "cdt")
7836  {
7837  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7838  }
7839  Utilities->CallLogPop();
7840  return(RetStr);
7841 }
7842 */
7843 // ---------------------------------------------------------------------------
7844 
7845 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
7846 { //last bool added at v2.13.2 so departure info adds random delay if actual rather than not timetable time required
7847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7848  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7849  AnsiString DepTime = "", EventTime = "";
7850  bool CDTFlag = false; //reports if train changes direction before departs
7851  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7852  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7853  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7854  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7855  {
7856  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7857  {
7858  TowardsLocation = AVI->LocationName;
7859  }
7860  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7861  {
7862  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7863  if(TE.ActiveTrackElementName != "")
7864  {
7865  TowardsLocation = TE.ActiveTrackElementName;
7866  }
7867  else
7868  {
7869  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7870  }
7871  }
7872  }
7873 
7874  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7875  {
7876  if(AVI->Command == "cdt")
7877  {
7878  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7879  continue;
7880  }
7881  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7882  {
7883  TDateTime TTTime = TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes);
7884  if((DelayedRandMins >= 1) && !TimetableTime)
7885  {
7886  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7887  }
7888  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7889  {
7891  }
7892  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7893  {
7894  EventTime = Utilities->Format96HHMM(TTTime);
7895  }
7896  RetStr += "\nNew service splits at approx. " + EventTime;
7897  Utilities->CallLogPop(2234);
7898  return(RetStr);
7899  }
7900  if(AVI->Command == "jbo") //added at v2.15.0
7901  {
7902  TDateTime TTTime = TrainController->GetControllerTrainTime(28, AVI->EventTime, RptNum, IncrementalMinutes);
7903  if((DelayedRandMins >= 1) && !TimetableTime)
7904  {
7905  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7906  }
7907  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7908  {
7910  }
7911  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7912  {
7913  EventTime = Utilities->Format96HHMM(TTTime);
7914  }
7915  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at approx. " + EventTime;
7916  Utilities->CallLogPop(2595);
7917  return(RetStr);
7918  }
7919  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh")) //added at v2.15.0
7920  {
7921  TDateTime TTTime = TrainController->GetControllerTrainTime(29, AVI->EventTime, RptNum, IncrementalMinutes);
7922  if((DelayedRandMins >= 1) && !TimetableTime)
7923  {
7924  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7925  }
7926  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7927  {
7929  }
7930  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7931  {
7932  EventTime = Utilities->Format96HHMM(TTTime);
7933  }
7934  RetStr += "\nNew service finishes and forms another new service at approx. " + EventTime;
7935  Utilities->CallLogPop(2615);
7936  return(RetStr);
7937  }
7938  if(AVI->Command == "Fjo") //added at v2.15.0
7939  {
7940  TDateTime TTTime = TrainController->GetControllerTrainTime(26, AVI->EventTime, RptNum, IncrementalMinutes);
7941  if((DelayedRandMins >= 1) && !TimetableTime)
7942  {
7943  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7944  }
7945  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7946  {
7948  }
7949  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7950  {
7951  EventTime = Utilities->Format96HHMM(TTTime);
7952  }
7953  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at approx. " + EventTime;
7954  Utilities->CallLogPop(2605);
7955  return(RetStr);
7956  }
7957  if(AVI->Command == "Frh") //added at v2.15.0
7958  {
7959  RetStr += "\nNew service finishes and remains at the location.";
7960  Utilities->CallLogPop(2606);
7961  return(RetStr);
7962  }
7963  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7964  {
7965  if(TimetableTime) //don't add random delay
7966  {
7967  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7968  if(CDTFlag)
7969  {
7970  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7971  {
7972  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7973  }
7974  else
7975  {
7976  RetStr += "\nNew service changes direction then departs at " + DepTime;
7977  }
7978  }
7979  else
7980  {
7981  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7982  {
7983  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7984  }
7985  else
7986  {
7987  RetStr += "\nNew service departs at " + DepTime;
7988  }
7989  }
7990  }
7991  else if(DelayedRandMins >= 1)//add random delay
7992  {
7993  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(24, AVI->DepartureTime + TDateTime(DelayedRandMins/1440), RptNum, IncrementalMinutes));
7994  if(CDTFlag)
7995  {
7996  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7997  {
7998  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7999  }
8000  else
8001  {
8002  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
8003  }
8004  }
8005  else
8006  {
8007  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
8008  {
8009  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
8010  }
8011  else
8012  {
8013  RetStr += "\nNew service departs at approx. " + DepTime;
8014  }
8015  }
8016  }
8017  else //no random delay but may be delayed for other reasons
8018  {
8019  TDateTime TTTime = TrainController->GetControllerTrainTime(25, AVI->DepartureTime, RptNum, IncrementalMinutes);
8020  if(TrainController->TTClockTime > TTTime)
8021  {
8023  }
8024  else
8025  {
8026  DepTime = Utilities->Format96HHMM(TTTime);
8027  }
8028  if(CDTFlag)
8029  {
8030  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
8031  {
8032  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
8033  }
8034  else
8035  {
8036  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
8037  }
8038  }
8039  else
8040  {
8041  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
8042  {
8043  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
8044  }
8045  else
8046  {
8047  RetStr += "\nNew service departs at approx. " + DepTime;
8048  }
8049  }
8050  }
8051  Utilities->CallLogPop(2236);
8052  return(RetStr);
8053  }
8054  }
8055  Utilities->CallLogPop(2208);
8056  return(RetStr); //if reach here then RetStr doesn't change
8057 }
8058 
8059 // ---------------------------------------------------------------------------
8060 
8062 // Enter with Ptr pointing to first action to be listed (i.e. next action)
8063 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
8064 {
8065  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
8066  ",FloatingTimetableString" + "," + HeadCode);
8067  AnsiString RetStr = "", PartStr = "";
8068  int Count = 0;
8069  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
8070  AnsiString LocName = Ptr->LocationName;
8071 
8072  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
8073  // can start in signaller control so exclude this
8074  {
8075  throw Exception("Error - start entry in FloatingTimetableString");
8076  }
8077  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
8078  bool FirstPass = true;
8079  Ptr--; // because incremented at start of loop
8080 
8081  // different first TimeTimeLoc display if in signaller control
8082  do
8083  {
8084  Ptr++;
8085  if((Ptr->FormatType == Repeat) || TimetableFinished)
8086  {
8087  break;
8088  }
8089  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
8090  {
8091  AnsiString TrainLoc = "";
8092  if(TrainMode == Timetable)
8093  {
8094  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
8095  {
8096  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8097  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8098  {
8099  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8100  }
8101  }
8102  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8103  {
8104  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8105  }
8106  else
8107  {
8108  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8109  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8110  Count++; // because there are 2 entries
8111  }
8112  }
8113  else // TrainMode == Signaller
8114  {
8115  if(DepartureTimeSet)
8116  {
8117  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8118  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8119  {
8120  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8121  }
8122  }
8123  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8124  {
8125  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8126  }
8127  else
8128  {
8129  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8130  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8131  Count++; // because there are 2 entries
8132  }
8133  }
8134  }
8135  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
8136  {
8137  AnsiString TrainLoc = "";
8138  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
8139  {
8140  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8141  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8142  {
8143  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8144  }
8145  }
8146  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8147  {
8148  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8149  }
8150  else
8151  {
8152  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8153  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8154  Count++; // because there are 2 entries
8155  }
8156  }
8157  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
8158  {
8159  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
8160  }
8161  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
8162  {
8163  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8164  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8165  {
8166  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8167  }
8168  }
8169  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
8170  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
8171  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
8172  }
8173  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
8174  {
8175  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
8176  }
8177  else if(Ptr->Command == "Fns")
8178  {
8179  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
8180  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8181  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to PartStr
8182  }
8183  else if(Ptr->Command == "F-nshs")
8184  {
8185  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
8186  Ptr->LocationName;
8187  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8188  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
8189  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
8190  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
8191  }
8192  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
8193  {
8194  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
8195  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8196  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8197  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8198  }
8199  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8200  {
8201  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
8202  +" at " + Ptr->LocationName;
8203  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8204  }
8205  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
8206  {
8207  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
8208  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8209  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8210  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8211  }
8212  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8213  {
8214  PartStr = "Terminate at " + Ptr->LocationName;
8215  }
8216  else if(Ptr->Command == "Frh")
8217  {
8218  PartStr = "Terminate at " + Ptr->LocationName;
8219  }
8220  else if(Ptr->Command == "Fer")
8221  {
8222  AnsiString AllowedExits = "";
8223  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
8224  }
8225  else if(Ptr->Command == "Fjo")
8226  {
8227  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
8228  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8229  }
8230  else if(Ptr->Command == "jbo")
8231  {
8232  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
8233  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8234  }
8235  else if(Ptr->Command == "fsp")
8236  {
8237  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
8238  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8239  if(Ptr->SplitDistribution != "")
8240  {
8241  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
8242  }
8243  else
8244  {
8245  PartStr+= ", split mass%-Power% = 50-50";
8246  }
8247  }
8248  else if(Ptr->Command == "rsp")
8249  {
8250  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
8251  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8252  if(Ptr->SplitDistribution != "")
8253  {
8254  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
8255  }
8256  else
8257  {
8258  PartStr+= ", split mass%-Power% = 50-50";
8259  }
8260  }
8261  else if(Ptr->Command == "cdt")
8262  {
8263  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
8264  }
8265  else if(Ptr->Command == "dsc")
8266  {
8267  PartStr = Utilities->Format96HHMM(GetTrainTime(67, Ptr->EventTime)) + ": Change description at " + Ptr->LocationName;
8268  }
8269  if(RetStr != "")
8270  {
8271  RetStr = RetStr + '\n' + PartStr;
8272  }
8273  else
8274  {
8275  RetStr = PartStr;
8276  }
8277  FirstPass = false;
8278  Count++;
8279 
8280  if(SkipDep)
8281  {
8282  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
8283  Ptr--; //it is incremented at the start of the next loop
8284  SkipDep = false;
8285  SkipDepActedOn = true;
8286  }
8287  }
8288  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8289  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
8290  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8291  // forward as anyone should wish to see without looking at the full timetable
8292  if(TimetableFinished)
8293  {
8294  if(TrainMode == Timetable)
8295  {
8296  RetStr = "Timetable finished";
8297  }
8298  else
8299  {
8300  RetStr = "No timetable";
8301  }
8302  }
8303  Utilities->CallLogPop(1125);
8304  return("Timetable:\n" + RetStr);
8305 }
8306 
8307 // ---------------------------------------------------------------------------
8308 
8309 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
8310 {
8311  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
8312  Utilities->SaveFileString(OutFile, HeadCode);
8315  Utilities->SaveFileInt(OutFile, StartSpeed);
8318  Utilities->SaveFileInt(OutFile, RepeatNumber);
8321  Utilities->SaveFileInt(OutFile, Mass);
8324  Utilities->SaveFileDouble(OutFile, EntrySpeed);
8331  Utilities->SaveFileDouble(OutFile, BrakeRate);
8335  Utilities->SaveFileDouble(OutFile, double(EntryTime));
8336  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
8337  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
8338  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
8339  Utilities->SaveFileDouble(OutFile, double(TRSTime));
8340  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
8344  Utilities->SaveFileInt(OutFile, (short)TrainMode);
8349  Utilities->SaveFileBool(OutFile, Derailed);
8351  Utilities->SaveFileBool(OutFile, Crashed);
8358  Utilities->SaveFileBool(OutFile, NotInService);
8359  Utilities->SaveFileBool(OutFile, Plotted);
8360  Utilities->SaveFileBool(OutFile, TrainGone);
8361  Utilities->SaveFileBool(OutFile, SPADFlag);
8363  Utilities->SaveFileInt(OutFile, HOffset[0]);
8364  Utilities->SaveFileInt(OutFile, HOffset[1]);
8365  Utilities->SaveFileInt(OutFile, HOffset[2]);
8366  Utilities->SaveFileInt(OutFile, HOffset[3]);
8367  Utilities->SaveFileInt(OutFile, VOffset[0]);
8368  Utilities->SaveFileInt(OutFile, VOffset[1]);
8369  Utilities->SaveFileInt(OutFile, VOffset[2]);
8370  Utilities->SaveFileInt(OutFile, VOffset[3]);
8371  Utilities->SaveFileInt(OutFile, PlotElement[0]);
8372  Utilities->SaveFileInt(OutFile, PlotElement[1]);
8373  Utilities->SaveFileInt(OutFile, PlotElement[2]);
8374  Utilities->SaveFileInt(OutFile, PlotElement[3]);
8375  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
8376  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
8377  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
8378  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
8380  Utilities->SaveFileInt(OutFile, (short)Straddle);
8381  Utilities->SaveFileInt(OutFile, NextTrainID);
8382  Utilities->SaveFileInt(OutFile, TrainID);
8383  Utilities->SaveFileInt(OutFile, LeadElement);
8384  Utilities->SaveFileInt(OutFile, LeadEntryPos);
8385  Utilities->SaveFileInt(OutFile, LeadExitPos);
8386  Utilities->SaveFileInt(OutFile, MidElement);
8387  Utilities->SaveFileInt(OutFile, MidEntryPos);
8388  Utilities->SaveFileInt(OutFile, MidExitPos);
8389  Utilities->SaveFileInt(OutFile, LagElement);
8390  Utilities->SaveFileInt(OutFile, LagEntryPos);
8391  Utilities->SaveFileInt(OutFile, LagExitPos);
8392  int ColourNumber;
8393 
8395  {
8396  ColourNumber = 0;
8397  }
8399  {
8400  ColourNumber = 1;
8401  }
8403  {
8404  ColourNumber = 2;
8405  }
8407  {
8408  ColourNumber = 3;
8409  }
8411  {
8412  ColourNumber = 4;
8413  }
8415  {
8416  ColourNumber = 5;
8417  }
8419  {
8420  ColourNumber = 6;
8421  }
8423  {
8424  ColourNumber = 7;
8425  }
8427  {
8428  ColourNumber = 8;
8429  }
8431  {
8432  ColourNumber = 9;
8433  }
8435  {
8436  ColourNumber = 10;
8437  }
8439  {
8440  ColourNumber = 11;
8441  }
8443  {
8444  ColourNumber = 12;
8445  }
8446  else if(BackgroundColour == clTRSBackground)
8447  {
8448  ColourNumber = 13;
8449  }
8451  {
8452  ColourNumber = 14; // added at v2.4.0
8453  }
8454  Utilities->SaveFileInt(OutFile, ColourNumber);
8455 
8456  // additional data
8457  bool ForwardHeadCode;
8458 
8459  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8460  {
8461  ForwardHeadCode = true;
8462  }
8463  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8464  else
8465  {
8466  ForwardHeadCode = false;
8467  }
8468  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8469 
8470  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8471 
8472  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8473  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8474 
8475  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8476  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8477  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8478  // so use the last asterisk position for this - 0 for false & 1 for true
8479  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8480  AnsiString Marker;
8481 
8483  {
8484  Marker = "*****1";
8485  }
8486  else
8487  {
8488  Marker = "*****0";
8489  }
8490  if(RestoreTimetableLocation == "")
8491  {
8492  Utilities->SaveFileString(OutFile, Marker);
8493  }
8494  else
8495  {
8496  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8497  Utilities->SaveFileString(OutFile, CombinedString);
8498  // RestoreTimetableLocation + marker
8499  }
8500  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8501  Utilities->CallLogPop(1457);
8502 }
8503 
8504 // ---------------------------------------------------------------------------
8505 
8506 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8507 {
8508  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8509  HeadCode = Utilities->LoadFileString(InFile);
8512  StartSpeed = Utilities->LoadFileInt(InFile);
8514  if(SignallerMaxSpeed < 10)
8515  {
8516  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8517  }
8519  RepeatNumber = Utilities->LoadFileInt(InFile);
8522  Mass = Utilities->LoadFileInt(InFile);
8525  {
8527  }
8528  // above added at v2.1.0 for legacy session files where value may not have been limited
8530  EntrySpeed = Utilities->LoadFileDouble(InFile);
8534  if(TimetableMaxRunningSpeed < 10)
8535  {
8536  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8537  }
8539  if(MaxRunningSpeed < 10)
8540  {
8541  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8542  }
8545  BrakeRate = Utilities->LoadFileDouble(InFile);
8549  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8550  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8551  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8552  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8553  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8554  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8563  Derailed = Utilities->LoadFileBool(InFile);
8565  Crashed = Utilities->LoadFileBool(InFile);
8572  NotInService = Utilities->LoadFileBool(InFile);
8573  Plotted = Utilities->LoadFileBool(InFile);
8574  TrainGone = Utilities->LoadFileBool(InFile);
8575  SPADFlag = Utilities->LoadFileBool(InFile);
8577  HOffset[0] = Utilities->LoadFileInt(InFile);
8578  HOffset[1] = Utilities->LoadFileInt(InFile);
8579  HOffset[2] = Utilities->LoadFileInt(InFile);
8580  HOffset[3] = Utilities->LoadFileInt(InFile);
8581  VOffset[0] = Utilities->LoadFileInt(InFile);
8582  VOffset[1] = Utilities->LoadFileInt(InFile);
8583  VOffset[2] = Utilities->LoadFileInt(InFile);
8584  VOffset[3] = Utilities->LoadFileInt(InFile);
8585  PlotElement[0] = Utilities->LoadFileInt(InFile);
8586  PlotElement[1] = Utilities->LoadFileInt(InFile);
8587  PlotElement[2] = Utilities->LoadFileInt(InFile);
8588  PlotElement[3] = Utilities->LoadFileInt(InFile);
8589  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8590  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8591  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8592  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8594  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8595  NextTrainID = Utilities->LoadFileInt(InFile);
8596  // will be same for all but best to save all anyway
8597  TrainID = Utilities->LoadFileInt(InFile);
8598  LeadElement = Utilities->LoadFileInt(InFile);
8599  LeadEntryPos = Utilities->LoadFileInt(InFile);
8600  LeadExitPos = Utilities->LoadFileInt(InFile);
8601  MidElement = Utilities->LoadFileInt(InFile);
8602  MidEntryPos = Utilities->LoadFileInt(InFile);
8603  MidExitPos = Utilities->LoadFileInt(InFile);
8604  LagElement = Utilities->LoadFileInt(InFile);
8605  LagEntryPos = Utilities->LoadFileInt(InFile);
8606  LagExitPos = Utilities->LoadFileInt(InFile);
8607  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8608 
8609  if(ColourNumber == 0)
8610  {
8612  }
8613  else if(ColourNumber == 1)
8614  {
8616  }
8617  else if(ColourNumber == 2)
8618  {
8620  }
8621  else if(ColourNumber == 3)
8622  {
8624  }
8625  else if(ColourNumber == 4)
8626  {
8628  }
8629  else if(ColourNumber == 5)
8630  {
8632  }
8633  else if(ColourNumber == 6)
8634  {
8636  }
8637  else if(ColourNumber == 7)
8638  {
8640  }
8641  else if(ColourNumber == 8)
8642  {
8644  }
8645  else if(ColourNumber == 9)
8646  {
8648  }
8649  else if(ColourNumber == 10)
8650  {
8652  }
8653  else if(ColourNumber == 11)
8654  {
8656  }
8657  else if(ColourNumber == 12)
8658  {
8660  }
8661  else if(ColourNumber == 13)
8662  {
8664  }
8665  else if(ColourNumber == 14)
8666  {
8667  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8668 
8669  }
8670  // additional data
8672  // sets the BackgroundColour to the loaded value
8673  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8674 
8675  if(ForwardHeadCode)
8676  {
8677  for(int x = 0; x < 4; x++)
8678  {
8680  }
8681  }
8682  else
8683  {
8684  for(int x = 0; x < 4; x++)
8685  {
8686  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8687  }
8688  }
8689  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8690  if(TrainMode == Timetable)
8691  {
8692  if(Crashed)
8693  {
8695  }
8696  else
8697  {
8699  }
8700  }
8701  else
8702  {
8704  }
8706  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8707  if(Straddle == LeadMid)
8708  {
8709  if(LeadElement > -1)
8710  {
8712  }
8713  if(LeadElement > -1)
8714  {
8716  }
8717  if(MidElement > -1)
8718  {
8720  }
8721  if(MidElement > -1)
8722  {
8724  }
8725  }
8726  else if(Straddle == LeadMidLag)
8727  {
8728  if(LeadElement > -1)
8729  {
8731  }
8732  if(MidElement > -1)
8733  {
8735  }
8736  if(MidElement > -1)
8737  {
8739  }
8740  if(LagElement > -1)
8741  {
8743  }
8744  }
8745  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8746 
8747  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8748  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8749 
8750  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8751 
8752  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8753  if(LeadElement > -1)
8754  // need to include this in case train exiting & no lead element
8755  {
8757  {
8758  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8759  }
8760  }
8761  AValue = sqrt(2 * PowerAtRail / Mass);
8762 
8763  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8764 
8765  // possible RestoreTimetableLocation + Marker, where Marker is
8766  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8767  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8768  // added at beta v0.2e
8769  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8770  // name not allowed to include the '*' character
8771  {
8772  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8773  bool GiveMessagesFalse = false;
8774  bool CheckLocationsExistInRailwayTrue = true;
8775  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8776  {
8777  // otherwise take no action
8778  RestoreTimetableLocation = Location;
8779  }
8780  }
8781  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8782 
8783  StoppedWithoutPower = false;
8784  if(Marker[6] == '1')
8785  {
8786  StoppedWithoutPower = true;
8787  }
8788  Utilities->CallLogPop(1458);
8789 }
8790 
8791 // ---------------------------------------------------------------------------
8792 
8793 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8794 {
8796  {
8797  return(false); // HeadCode
8798 
8799  }
8800  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8801  {
8802  return(false); // RearStartElement
8803 
8804  }
8805  if(!Utilities->CheckFileInt(InFile, 0, 3))
8806  {
8807  return(false); // RearStartExitPos
8808 
8809  }
8810  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8811  {
8812  return(false); // StartSpeed
8813 
8814  }
8815  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8816  {
8817  return(false); // SignallerMaxSpeed
8818 
8819  }
8820  if(!Utilities->CheckFileBool(InFile))
8821  {
8822  return(false); // HoldAtLocationInTTMode
8823 
8824  }
8825  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8826  {
8827  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8828 
8829  }
8830  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8831  {
8832  return(false); // IncrementalMinutes (max 96 x 60)
8833 
8834  }
8835  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8836  {
8837  return(false); // IncrementalDigits
8838 
8839  }
8840  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8841  {
8842  return(false); // Mass
8843 
8844  }
8845  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8846  {
8847  return(false);
8848  }
8849  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8850  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8851  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8852  {
8853  return(false); // FrontElementLength
8854 
8855  }
8856  if(!Utilities->CheckFileDouble(InFile))
8857  {
8858  return(false); // EntrySpeed
8859 
8860  }
8861  if(!Utilities->CheckFileDouble(InFile))
8862  {
8863  return(false); // ExitSpeedHalf
8864 
8865  }
8866  if(!Utilities->CheckFileDouble(InFile))
8867  {
8868  return(false); // ExitSpeedFull
8869 
8870  }
8871  if(!Utilities->CheckFileDouble(InFile))
8872  {
8873  return(false); // TimetableMaxRunningSpeed
8874 
8875  }
8876  if(!Utilities->CheckFileDouble(InFile))
8877  {
8878  return(false); // MaxRunningSpeed
8879 
8880  }
8881  if(!Utilities->CheckFileDouble(InFile))
8882  {
8883  return(false); // MaxExitSpeed
8884 
8885  }
8886  if(!Utilities->CheckFileDouble(InFile))
8887  {
8888  return(false); // MaxBrakeRate
8889 
8890  }
8891  if(!Utilities->CheckFileDouble(InFile))
8892  {
8893  return(false); // BrakeRate
8894 
8895  }
8896  if(!Utilities->CheckFileDouble(InFile))
8897  {
8898  return(false); // PowerAtRail
8899 
8900  }
8901  if(!Utilities->CheckFileBool(InFile))
8902  {
8903  return(false); // FirstHalfMove
8904 
8905  }
8906  if(!Utilities->CheckFileBool(InFile))
8907  {
8908  return(false); // OneLengthAccelDecel
8909 
8910  }
8911  if(!Utilities->CheckFileDouble(InFile))
8912  {
8913  return(false); // double(EntryTime)
8914 
8915  }
8916  if(!Utilities->CheckFileDouble(InFile))
8917  {
8918  return(false); // double(ExitTimeHalf)
8919 
8920  }
8921  if(!Utilities->CheckFileDouble(InFile))
8922  {
8923  return(false); // double(ExitTimeFull)
8924 
8925  }
8926  if(!Utilities->CheckFileDouble(InFile))
8927  {
8928  return(false); // double(ReleaseTime)
8929 
8930  }
8931  if(!Utilities->CheckFileDouble(InFile))
8932  {
8933  return(false); // double(TRSTime)
8934 
8935  }
8936  if(!Utilities->CheckFileDouble(InFile))
8937  {
8938  return(false); // double(LastActionTime)
8939 
8940  }
8941  if(!Utilities->CheckFileBool(InFile))
8942  {
8943  return(false); // CallingOnFlag
8944 
8945  }
8946  if(!Utilities->CheckFileBool(InFile))
8947  {
8948  return(false); // BeingCalledOn
8949 
8950  }
8951  if(!Utilities->CheckFileBool(InFile))
8952  {
8953  return(false); // DepartureTimeSet
8954 
8955  }
8956  if(!Utilities->CheckFileInt(InFile, 0, 2))
8957  {
8958  return(false); // (short)TrainMode
8959 
8960  }
8961  if(!Utilities->CheckFileBool(InFile))
8962  {
8963  return(false); // TimetableFinished
8964 
8965  }
8966  if(!Utilities->CheckFileBool(InFile))
8967  {
8968  return(false); // LastActionDelayFlag
8969 
8970  }
8971  if(!Utilities->CheckFileBool(InFile))
8972  {
8973  return(false); // SignallerRemoved
8974 
8975  }
8976  if(!Utilities->CheckFileBool(InFile))
8977  {
8978  return(false); // TerminatedMessageSent
8979 
8980  }
8981  if(!Utilities->CheckFileBool(InFile))
8982  {
8983  return(false); // Derailed
8984 
8985  }
8986  if(!Utilities->CheckFileBool(InFile))
8987  {
8988  return(false); // DerailPending
8989 
8990  }
8991  if(!Utilities->CheckFileBool(InFile))
8992  {
8993  return(false); // Crashed
8994 
8995  }
8996  if(!Utilities->CheckFileBool(InFile))
8997  {
8998  return(false); // StoppedAtBuffers
8999 
9000  }
9001  if(!Utilities->CheckFileBool(InFile))
9002  {
9003  return(false); // StoppedAtSignal
9004 
9005  }
9006  if(!Utilities->CheckFileBool(InFile))
9007  {
9008  return(false); // StoppedAtLocation
9009 
9010  }
9011  if(!Utilities->CheckFileBool(InFile))
9012  {
9013  return(false); // SignallerStopped
9014 
9015  }
9016  if(!Utilities->CheckFileBool(InFile))
9017  {
9018  return(false); // StoppedAfterSPAD
9019 
9020  }
9021  if(!Utilities->CheckFileBool(InFile))
9022  {
9023  return(false); // StoppedForTrainInFront
9024 
9025  }
9026  if(!Utilities->CheckFileBool(InFile))
9027  {
9028  return(false); // NotInService
9029 
9030  }
9031  if(!Utilities->CheckFileBool(InFile))
9032  {
9033  return(false); // Plotted
9034 
9035  }
9036  if(!Utilities->CheckFileBool(InFile))
9037  {
9038  return(false); // TrainGone
9039 
9040  }
9041  if(!Utilities->CheckFileBool(InFile))
9042  {
9043  return(false); // SPADFlag
9044 
9045  }
9046  if(!Utilities->CheckFileBool(InFile))
9047  {
9048  return(false); // TimeTimeLocArrived
9049 
9050  }
9051  if(!Utilities->CheckFileInt(InFile, 0, 15))
9052  {
9053  return(false); // HOffset[0]
9054 
9055  }
9056  if(!Utilities->CheckFileInt(InFile, 0, 15))
9057  {
9058  return(false); // HOffset[1]
9059 
9060  }
9061  if(!Utilities->CheckFileInt(InFile, 0, 15))
9062  {
9063  return(false); // HOffset[2]
9064 
9065  }
9066  if(!Utilities->CheckFileInt(InFile, 0, 15))
9067  {
9068  return(false); // HOffset[3]
9069 
9070  }
9071  if(!Utilities->CheckFileInt(InFile, 0, 15))
9072  {
9073  return(false); // VOffset[0]
9074 
9075  }
9076  if(!Utilities->CheckFileInt(InFile, 0, 15))
9077  {
9078  return(false); // VOffset[1]
9079 
9080  }
9081  if(!Utilities->CheckFileInt(InFile, 0, 15))
9082  {
9083  return(false); // VOffset[2]
9084 
9085  }
9086  if(!Utilities->CheckFileInt(InFile, 0, 15))
9087  {
9088  return(false); // VOffset[3]
9089 
9090  }
9091  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9092  {
9093  return(false); // PlotElement[0]
9094 
9095  }
9096  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9097  {
9098  return(false); // PlotElement[1]
9099 
9100  }
9101  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9102  {
9103  return(false); // PlotElement[2]
9104 
9105  }
9106  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9107  {
9108  return(false); // PlotElement[3]
9109 
9110  }
9111  if(!Utilities->CheckFileInt(InFile, 0, 3))
9112  {
9113  return(false); // PlotEntryPos[0]
9114 
9115  }
9116  if(!Utilities->CheckFileInt(InFile, 0, 3))
9117  {
9118  return(false); // PlotEntryPos[1]
9119 
9120  }
9121  if(!Utilities->CheckFileInt(InFile, 0, 3))
9122  {
9123  return(false); // PlotEntryPos[2]
9124 
9125  }
9126  if(!Utilities->CheckFileInt(InFile, 0, 3))
9127  {
9128  return(false); // PlotEntryPos[3]
9129 
9130  }
9131  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9132  {
9133  return(false); // TrainCrashedInto
9134 
9135  }
9136  if(!Utilities->CheckFileInt(InFile, 0, 2))
9137  {
9138  return(false); // (short)Straddle
9139 
9140  }
9141  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
9142  {
9143  return(false); // NextTrainID
9144 
9145  }
9146  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
9147  {
9148  return(false); // TrainID
9149 
9150  }
9151  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9152  {
9153  return(false); // LeadElement
9154 
9155  }
9156  if(!Utilities->CheckFileInt(InFile, 0, 3))
9157  {
9158  return(false); // LeadEntryPos
9159 
9160  }
9161  if(!Utilities->CheckFileInt(InFile, 0, 3))
9162  {
9163  return(false); // LeadExitPos
9164 
9165  }
9166  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9167  {
9168  return(false); // MidElement
9169 
9170  }
9171  if(!Utilities->CheckFileInt(InFile, 0, 3))
9172  {
9173  return(false); // MidEntryPos
9174 
9175  }
9176  if(!Utilities->CheckFileInt(InFile, 0, 3))
9177  {
9178  return(false); // MidExitPos
9179 
9180  }
9181  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9182  {
9183  return(false); // LagElement
9184 
9185  }
9186  if(!Utilities->CheckFileInt(InFile, 0, 3))
9187  {
9188  return(false); // LagEntryPos
9189 
9190  }
9191  if(!Utilities->CheckFileInt(InFile, 0, 3))
9192  {
9193  return(false); // LagExitPos
9194 
9195  }
9196  if(!Utilities->CheckFileInt(InFile, 0, 14))
9197  {
9198  return(false);
9199  }
9200  // Background colour number //14 is failed colour at v2.4.0
9201  if(!Utilities->CheckFileBool(InFile))
9202  {
9203  return(false); // ForwardHeadCode
9204 
9205  }
9206  if(!Utilities->CheckFileInt(InFile, 0, 10000))
9207  {
9208  return(false); // TrainDataEntryValue
9209 
9210  }
9211  if(!Utilities->CheckFileInt(InFile, 0, 10000))
9212  {
9213  return(false); // ActionVectorEntryValue
9214 
9215  }
9217  {
9218  return(false); // End of train marker + possible RestoreTimetableLocation
9219 
9220  }
9221  // and StoppedWithoutPower flag
9222  return(true);
9223 }
9224 
9225 // ---------------------------------------------------------------------------
9226 
9227 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
9228 {
9229  // order below reflects significance so earlier shows first, as may have more than one flag set
9230  // only plot flashing trains when Flash is true
9231 
9232 /*
9233  clCrashedBackground (TColor)0x0000FF red
9234  clDerailedBackground (TColor)0x0000FF red
9235  clSPADBackground (TColor)0x00FFFF yellow
9236  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
9237  clCallOnBackground (TColor)0xFF33FF light magenta
9238  clSignalStopBackground (TColor)0x00FF66 green
9239  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
9240  clStationStopBackground (TColor)0xCCFFCC pale green
9241  clTRSBackground (TColor)0xFFCCFF light pink
9242  clBufferStopBackground (TColor)0xFFFFCC pale cyan
9243  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
9244  clSignallerStopped (TColor)0x99CCFF caramel
9245  clNormalBackground (TColor)0xCCCCCC grey
9246 */
9247 
9248  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
9249  bool HideFlashingTrain = true;
9250  // hide it when Flash false so it blinks on and off
9251  // if don't hide it it stays displayed all the time
9252  Graphics::TBitmap *SmallTrainBitmap;
9253 
9254  // NB ensure retain same order as zoomed in order so colours correspond
9256  {
9257  TrainController->CrashWarning = true;
9258  SmallTrainBitmap = RailGraphics->smRed;
9259  }
9261  {
9263  SmallTrainBitmap = RailGraphics->smRed;
9264  }
9266  {
9267  TrainController->SPADWarning = true;
9268  SmallTrainBitmap = RailGraphics->smYellow;
9269  }
9271  {
9273  SmallTrainBitmap = RailGraphics->smOrange;
9274  }
9276  {
9278  SmallTrainBitmap = RailGraphics->smMagenta;
9279  }
9281  {
9283  SmallTrainBitmap = RailGraphics->smBrightGreen;
9284  }
9286  {
9288  SmallTrainBitmap = RailGraphics->smCyan;
9289  }
9291  {
9292  SmallTrainBitmap = RailGraphics->smPaleGreen;
9293  HideFlashingTrain = false;
9294  }
9296  {
9297  SmallTrainBitmap = RailGraphics->smCyan;
9298  HideFlashingTrain = false;
9299  }
9301  {
9302  SmallTrainBitmap = RailGraphics->smLightBlue;
9303  HideFlashingTrain = false;
9304  }
9306  {
9307  SmallTrainBitmap = RailGraphics->smCaramel;
9308  HideFlashingTrain = false;
9309  }
9310  else
9311  {
9312  SmallTrainBitmap = RailGraphics->smBlack; // moving
9313  HideFlashingTrain = false;
9314  }
9315  // now plot the new train
9316  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
9317  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
9318  {
9319  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
9320  }
9321  if((MidElement > -1) && (!HideFlashingTrain || Flash))
9322  {
9323  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
9324  }
9325  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
9326  {
9327  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
9328  }
9332  Utilities->CallLogPop(1459);
9333 }
9334 
9335 // ---------------------------------------------------------------------------
9336 
9338 {
9339  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
9340  if(!Display->ZoomOutFlag)
9341  {
9342  Utilities->CallLogPop(1304);
9343  return;
9344  }
9345  for(int y = 0; y < 3; y++)
9346  {
9347  if(OldZoomOutElement[y] > -1)
9348  {
9349  bool FoundFlag = false;
9350  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
9351  TTrackElement IATElement1, IATElement2;
9352  // default elements to begin with
9353  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
9354  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
9355  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
9356  if(FoundFlag)
9357  {
9358  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
9359  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
9360  if(IMPair.first != IMPair.second)
9361  {
9362  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
9363  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
9364  }
9365  }
9366  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
9367  }
9368  }
9369  Utilities->CallLogPop(1305);
9370 }
9371 
9372 // ---------------------------------------------------------------------------
9373 
9374 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
9375 {
9376  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
9377  LocationName = "";
9378  if(!RevisedStoppedAtLoc())
9379  {
9380  Utilities->CallLogPop(1398);
9381  return(false);
9382  }
9383  if(LeadElement > -1)
9384  {
9386  }
9387  if((LocationName == "") && (MidElement > -1))
9388  {
9389  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
9390  }
9391  if((LocationName == "") && (LagElement > -1))
9392  {
9393  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
9394  }
9395  if(LocationName == "")
9396  {
9397  throw Exception("Error - Location name not set in TrainAtLocation");
9398  }
9399  Utilities->CallLogPop(1399);
9400  return(true);
9401 }
9402 
9403 // ---------------------------------------------------------------------------
9404 
9405 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
9406 {
9407  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
9408  for(int x = 0; x < 4; x++)
9409  {
9410  PlotTrainGraphic(7, x, Disp);
9411  }
9412  Utilities->CallLogPop(647);
9413 }
9414 
9415 // ---------------------------------------------------------------------------
9416 
9417 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
9418 {
9419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
9420  for(int x = 0; x < 4; x++)
9421  {
9422  if(PlotElement[x] > -1)
9423  {
9424  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
9425  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
9426  }
9427  }
9428  Utilities->CallLogPop(1708);
9429 }
9430 
9431 // ---------------------------------------------------------------------------
9432 
9433 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
9434 {
9435  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
9436  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
9437  AnsiString(LinkNumber) + "," + HeadCode);
9438 
9439 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
9440  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
9441  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
9442 */
9443 
9444  // note that MidElement always fully occupied
9445  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
9446  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
9447  {
9448  Utilities->CallLogPop(2005);
9449  return(true);
9450  }
9451  if(Straddle == LeadMid)
9452  {
9453  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9454  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9455  {
9456  Utilities->CallLogPop(2006);
9457  return(true);
9458  }
9459  }
9460  else if(Straddle == LeadMidLag)
9461  {
9462  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9463  // only interested in LeadEntryPos as train not occupying ExitPos yet
9464  {
9465  Utilities->CallLogPop(2007);
9466  return(true);
9467  }
9468  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9469  // only interested in LagExitPos as train has left EntryPos
9470  {
9471  Utilities->CallLogPop(2008);
9472  return(true);
9473  }
9474  }
9475  Utilities->CallLogPop(2009);
9476  return(false);
9477 }
9478 
9479 // ---------------------------------------------------------------------------
9480 
9481 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9486 // TimeToExit & ExitPair added for multiplayer
9487 {
9488  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9489  int DistanceToRedSignal = 0, DistanceToExit = -1;
9490  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9491  TimeToExit = -1;
9492  ExitPair.first = -1;
9493  ExitPair.second = -1;
9494  float MinsEarly = 0; //added at v2.6.1
9495  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9496  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9497  float TempTTE;
9498 
9499  if(TrainFailed)
9500  {
9501  Utilities->CallLogPop(2147);
9502  return(0); // time to act now, time to exit set above
9503  }
9504  if(SignallerStopped)
9505  {
9506  Utilities->CallLogPop(2080);
9507  return(-1); //time to exit set above
9508  }
9509  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9510  {
9511  Utilities->CallLogPop(2266);
9512  return(-1); // time to exit set above
9513  }
9514 
9515  // check if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9516  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9517  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9518  {
9519  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9520  {
9521  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9522  {
9523  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9524  if(TempTTE < LastTimeToExit)
9525  {
9526  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9527  }
9528  else
9529  {
9530  TimeToExit = LastTimeToExit;
9531  }
9532  }
9533  else
9534  {
9535  TimeToExit = LastTimeToExit;
9536  }
9537  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9538  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9539  Utilities->CallLogPop(2342);
9540  return(-1);
9541  }
9542  else
9543  {
9544  TimeToExit = 0; //all train exited
9545  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9546  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9547  Utilities->CallLogPop(2343);
9548  return(-1);
9549  }
9550  }
9551  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9552  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9553  {
9554  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9555  {
9556  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9557  {
9558  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9559  if(TempTTE < LastTimeToExit)
9560  {
9561  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9562  }
9563  else
9564  {
9565  TimeToExit = LastTimeToExit;
9566  }
9567  }
9568  else
9569  {
9570  TimeToExit = LastTimeToExit;
9571  }
9572  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9573  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9574  Utilities->CallLogPop(2344);
9575  return(-1);
9576  }
9577  else //front element of train fully off the exit, one length to exit
9578  {
9579  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9580  {
9581  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9582  if(TempTTE < LastTimeToExit)
9583  {
9584  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9585  }
9586  else
9587  {
9588  TimeToExit = LastTimeToExit;
9589  }
9590  }
9591  else
9592  {
9593  TimeToExit = LastTimeToExit;
9594  }
9595  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9596  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9597  Utilities->CallLogPop(2345);
9598  return(-1);
9599  }
9600  }
9601  if(LeadElement > -1)
9602  {
9604  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9605  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9606  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9607  if(Straddle == LeadMidLag)
9608  {
9609  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9610  {
9611  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9612  if(TempTTE < LastTimeToExit)
9613  {
9614  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9615  }
9616  else
9617  {
9618  TimeToExit = LastTimeToExit;
9619  }
9620  }
9621  else
9622  {
9623  TimeToExit = LastTimeToExit;
9624  }
9625  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9626  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9627  Utilities->CallLogPop(2346);
9628  return(-1);
9629  }
9630  else
9631  {
9632  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9633  {
9634  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9635  if(TempTTE < LastTimeToExit)
9636  {
9637  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9638  }
9639  else
9640  {
9641  TimeToExit = LastTimeToExit;
9642  }
9643  }
9644  else
9645  {
9646  TimeToExit = LastTimeToExit;
9647  }
9648  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9649  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9650  Utilities->CallLogPop(2347);
9651  return(-1);
9652  }
9653  }
9654  }
9655 //here if LeadElement > -1 and its forward connection also > -1
9656 
9657  // calc distance to next red signal
9658  if(!Stopped() || RevisedStoppedAtLoc())
9659  {
9660  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9661  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9662  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9663 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9664  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9665  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9666  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9667  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9668  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9669  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9670  before the train has stopped the current station is still recognised as a future stop.
9671  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9672  becomes the signal, and the time to act indication becomes 'NOW'.
9673 */
9674  {
9675  FirstPosToBeMeasured = LeadElement;
9676  FirstEntryPos = LeadEntryPos;
9677  }
9678  float CurrentStopTime; // set to 0 at start of function
9679  float LaterStopTime; // set to 0 at start of function
9680  float RecoverableTime; // set to 0 at start of function
9681  int AvTrackSpeed; // set to zero at start of function
9682  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9683  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9684  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9685 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9686 //Therefore since need to calculate the time for each in the same way use a generic
9687 
9688  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9689  {
9690  TimeToExit = -1;
9691  Utilities->CallLogPop(2076);
9692  return(-1);
9693  }
9694 //else one or other is set
9695  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9696  bool DistanceToExitSet = (DistanceToExit > -1);
9697  int GenericDistance = DistanceToRedSignal;
9698  if(DistanceToExitSet)
9699  {
9700  GenericDistance = DistanceToExit;
9701  }
9702 /* Have MinsDelayed; pos or neg,
9703  CurrentStopTime; pos or zero
9704  LaterStopTime; pos or zero
9705  RecoverableTime; pos or zero
9706 
9707  & from these calculate TotalStopTime. noting that:
9708  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9709  RecoverableTime always < LaterStopTime or both zero
9710  can't subtract more than RecoverableTime (MinsDelayed > 0)
9711  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9712  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9713  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9714  if running early & stopped at location CurrentStopTime will automatically include the excess
9715 */
9716  float TimeToSubtract, TotalStopTime;
9717  if(MinsDelayed > RecoverableTime)
9718  {
9719  TimeToSubtract = RecoverableTime;
9720  }
9721  else
9722  {
9723  TimeToSubtract = MinsDelayed; // may be negative;
9724  }
9725  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9726  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9727  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9728  //next station stop
9729  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9730  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9731  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9732  //first find departure time from the next stop
9733  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9734  {
9735  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9736  {
9739  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9740  }
9741  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9742  {
9744  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9745  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9746  {
9747  // must be a departure
9748  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9749  }
9750  }
9751  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9752  {
9753  MinsEarly = 0;
9754  }
9755  if(MinsEarly < 0)
9756  {
9757  MinsEarly = 0;
9758  }
9759  }
9760  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9761  {
9762  if(CurrentStopTime > 0)
9763  {
9764  TotalStopTime = CurrentStopTime + LaterStopTime;
9765  }
9766  // stopped at loc, will depart on time
9767  else
9768  {
9769  TotalStopTime = LaterStopTime - MinsDelayed;
9770  }
9771  // not stopped, will depart on time at first later stop so add the delay
9772  }
9773  else if((MinsEarly > 0) && !Stopped()) //running early
9774  {
9775  TotalStopTime = LaterStopTime + MinsEarly;
9776  }
9777  else // on time or running late
9778  {
9779  if(LaterStopTime == 0)
9780  {
9781  TotalStopTime = CurrentStopTime;
9782  }
9783  // no later stops, if stopped now will depart as soon as possible,
9784  // if not stopped no stop times to add
9785  else
9786  {
9787  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9788  }
9789  }
9790  if(AvTrackSpeed < 30)
9791  {
9792  AvTrackSpeed = 30;
9793  }
9794  int Speed = AvTrackSpeed;
9795  if(AvTrackSpeed > int(MaxRunningSpeed))
9796  {
9797  Speed = int(MaxRunningSpeed);
9798  }
9799  if(TrainMode == Signaller)
9800  {
9801  Speed = SignallerMaxSpeed;
9802  TotalStopTime = 0;
9803  }
9804  if(DistanceToRedSignalSet)
9805  {
9806  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9807  // accel & decel taken into account in
9808  // CalcDistanceToRedSignalandStopTime
9809  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9810  TimeToExit = -1;
9811  Utilities->CallLogPop(2079);
9812  return(TimeToAct);
9813  }
9814  else //DistanceToExitSet must be true
9815  {
9816  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9817  // accel & decel taken into account in
9818  // CalcDistanceToRedSignalandStopTime
9819  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9820  Utilities->CallLogPop(2370);
9821  return(-1); //no red signal so no time to act
9822  }
9823  }
9824  else // stopped not at location
9825  {
9827  {
9828  TimeToAct = 0.0;
9829  TimeToExit = -1;
9830  }
9831  if(StoppedWithoutPower) //added at v2.13.2 as this situation was missed & time to act was 0 [If train failed then covered above]
9832  {
9833  TimeToAct = -1;
9834  TimeToExit = -1;
9835  }
9836  // but if stopped at a signal & autosigs route after it then ok, provided signal not failed
9837  if(StoppedAtSignal)
9838  {
9839  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9840  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9841  bool NextElementFailed = Track->TrackElementAt(1548, NextElement).Failed; //added at v2.13.2
9842  int NextExitPos;
9843  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9844  {
9845  if((NextEntryPos == 0) || (NextEntryPos == 2))
9846  // leading entry point
9847  {
9848  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9849  {
9850  NextExitPos = 1;
9851  }
9852  else
9853  {
9854  NextExitPos = 3;
9855  }
9856  }
9857  else
9858  {
9859  NextExitPos = 0; // trailing entry point
9860  }
9861  }
9862  else
9863  {
9864  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9865  }
9866  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9867  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9868  int RouteNumber; // holder for referenced value, not used
9869  if((AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !NextElementFailed)
9870  { //NextElementFailed added at v2.13.2
9871  TimeToAct = -1;
9872  TimeToExit = -1;
9873  }
9874  }
9875  Utilities->CallLogPop(2074);
9876  return(TimeToAct);
9877  }
9878 }
9879 
9880 // ---------------------------------------------------------------------------
9881 
9883 {
9884  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9885  if(LeadElement > -1)
9886  {
9888  {
9889  Utilities->CallLogPop(2148);
9890  return(true);
9891  }
9892  }
9893  if(MidElement > -1)
9894  {
9896  {
9897  Utilities->CallLogPop(2149);
9898  return(true);
9899  }
9900  }
9901  if(LagElement > -1)
9902  {
9904  {
9905  Utilities->CallLogPop(2150);
9906  return(true);
9907  }
9908  }
9909  Utilities->CallLogPop(2151);
9910  return(false);
9911 }
9912 
9913 // ---------------------------------------------------------------------------
9914 // TTrainController
9915 // ---------------------------------------------------------------------------
9916 
9918 {
9919  OnTimeArrivals = 0;
9920  LateArrivals = 0;
9921  EarlyArrivals = 0;
9922  OnTimePasses = 0;
9923  LatePasses = 0;
9924  EarlyPasses = 0;
9925  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9926  LateExits = 0;
9927  EarlyExits = 0;
9928  OnTimeDeps = 0;
9929  LateDeps = 0;
9930  MissedStops = 0;
9931  OtherMissedEvents = 0;
9932  UnexpectedExits = 0;
9933  NumFailures = 0;
9934  IncorrectExits = 0;
9935  SPADEvents = 0;
9936  SPADRisks = 0;
9937  CrashedTrains = 0;
9938  Derailments = 0;
9939  TotArrDepPass = 0;
9940  TotLateArrMins = 0;
9941  TotEarlyArrMins = 0;
9942  TotLatePassMins = 0;
9943  TotEarlyPassMins = 0;
9944  TotLateExitMins = 0; //added at v2.9.1
9945  TotEarlyExitMins = 0; //added at v2.9.1
9946  TotLateDepMins = 0;
9947  ExcessLCDownMins = 0;
9948  SkippedTTEvents = 0; //added at v2.11.0
9949  TTClockTime = 0; // added for v0.6
9951  // added at v1.3.0 to ensure false at start
9952  OpTimeToActUpdateCounter = 0; // new v2.2.0
9953  OpActionPanelVisible = false; // new v2.2.0
9954  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9955  SSHigh = false;
9956  MRSHigh = false;
9957  MRSLow = false;
9958  MassHigh = false;
9959  BFHigh = false;
9960  BFLow = false;
9961  PwrHigh = false;
9962  SigSHigh = false;
9963  SigSLow = false;
9964  randomize();
9965  // to seed rand() & random() with a random number (see UpdateTrain)
9966 }
9967 
9968 // ---------------------------------------------------------------------------
9969 
9971 {
9972  for(unsigned int x = 0; x < TrainVector.size(); x++)
9973  {
9974  TrainVectorAt(32, x).DeleteTrain(4);
9975  }
9976  TrainVector.clear();
9977 }
9978 
9979 // ---------------------------------------------------------------------------
9980 
9981 void TTrainController::LogEvent(AnsiString Str)
9982 {
9983  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9984 
9985  // restrict to last 1000 entries
9986  Utilities->EventLog.push_back(FullStr);
9987  if(Utilities->EventLog.size() > 1000)
9988  {
9989  Utilities->EventLog.pop_front();
9990  }
9991 }
9992 
9993 // ---------------------------------------------------------------------------
9994 
9996 {
9997  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9998  bool ClockState = Utilities->Clock2Stopped;
9999  Utilities->Clock2Stopped = true;
10000  // new section dealing with Snt & Snt-sh additions
10001  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
10002  // clock tick after stops flashing
10004  {
10005  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
10006  {
10007  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
10008  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
10009  TActionEventType EventType = NoEvent;
10010  if(AVEntry0.Command == "Snt")
10011  {
10012  // calc below only for Snt & Snt-sh entries rather than all entries to save time
10013  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
10014  int IncrementalMinutes = 0;
10015  int IncrementalDigits = 0;
10016  if(AVEntryLast.FormatType == Repeat)
10017  {
10018  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
10019  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
10020  }
10021  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
10022  {
10023  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
10024  }
10025  // see above note
10026 
10027  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10028  {
10029  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
10030  if(TTOD.RunningEntry != NotStarted)
10031  {
10032  continue;
10033  }
10034 
10035 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
10036 //if so and no arrival signalled yet bypass the timetabled arrival
10037 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
10038 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
10039 
10040  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
10041  {
10042  break; // all the rest will also be greater
10043  }
10044  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
10045  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
10046  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
10047  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
10048  {
10049  TTOD.TrainID = TrainVector.back().TrainID;
10050  TTOD.RunningEntry = Running;
10051  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
10052  }
10053  else if(EventType == FailTrainEntry)
10054  {
10055  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
10056  }
10057  }
10058  }
10059  if(AVEntry0.Command == "Snt-sh")
10060  // just start this once, shuttle repeats take care of restarts
10061  {
10062  // calc below only for Snt & Snt-sh entries rather than all entries to save time
10063  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
10064  int IncrementalMinutes = 0;
10065  int IncrementalDigits = 0;
10066  if(AVEntryLast.FormatType == Repeat)
10067  {
10068  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
10069  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
10070  }
10071  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
10072  {
10073  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
10074  }
10075  // see above note
10076  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
10077  if(TTOD.RunningEntry == NotStarted)
10078  {
10079  if(AVEntry0.EventTime <= TTClockTime)
10080  {
10081  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
10082  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
10083  TDEntry.SignallerSpeed, false, EventType))
10084  // false for SignallerControl
10085  {
10086  TTOD.TrainID = TrainVector.back().TrainID;
10087  TTOD.RunningEntry = Running;
10088  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
10089  }
10090  else if(EventType == FailTrainEntry)
10091  {
10092  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
10093  }
10094  }
10095  }
10096  }
10097  }
10098  }
10099 
10100  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
10101  // iteration, next cycle will catch up with any other pending updates
10102  if(!TrainVector.empty())
10103  {
10104  TrainAdded = false;
10105 
10106 //elapsed time investigations
10107 
10108 //elapsed time segment
10109 //double Start, End;
10110 //AnsiString ElapsedTimeReport = "";
10111 //end elasped time segment
10112  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
10113 //elapsed time segment
10114 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
10115 //PerfLogForm->PerformanceLog(-1, "Start time list");
10116 //end elapsed time segment
10117  for(unsigned int x = 0; x < TrainVector.size(); x++)
10118  {
10119 //elapsed time segment
10120 //Start = double(GetTime()) * 86400; //secs
10121 //end elapsed time segment
10122  TrainVectorAt(33, x).UpdateTrain(0);
10123 //elapsed time segment
10124 //End = double(GetTime()) * 86400;
10125 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
10126 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
10127 //end elapsed time segment
10128 
10129 //end elapsed time investigations
10130 
10131  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
10132  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
10133  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
10134  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
10135  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
10136  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
10137  so it doesn't plot trains with TrainGone set, but left this is as does no harm
10138 
10139  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
10140  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
10141  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
10142 
10143  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
10144  */
10145  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
10146  {
10147  break; //only one exited train will be dealt with at a time (see below) so no point looking further
10148  }
10149  }
10150  // set warning flags (ManualLCDownAttentionWarning dealt with in InterfaceUnit)
10151  CrashWarning = false;
10152  DerailWarning = false;
10153  SPADWarning = false;
10154  CallOnWarning = false;
10155  SignalStopWarning = false;
10156  BufferAttentionWarning = false;
10157  TrainFailedWarning = false;
10158  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
10159  {
10160  TTrain &Train = TrainVectorAt(34, x);
10161  if(Train.Crashed)
10162  // can't use background colours for crashed & derailed because same colour
10163  {
10164  CrashWarning = true;
10165  }
10166  else if(Train.Derailed)
10167  // can't use background colours for crashed & derailed because same colour
10168  {
10169  DerailWarning = true;
10170  }
10171  else if(Train.BackgroundColour == clSPADBackground)
10172  // use colour as that changes as soon as passes signal
10173  {
10174  SPADWarning = true;
10175  }
10176  else if(Train.BackgroundColour == clTrainFailedBackground)
10177  {
10178  TrainFailedWarning = true;
10179  }
10180  else if(Train.BackgroundColour == clCallOnBackground)
10181  // use colour as also stopped at signal
10182  {
10183  CallOnWarning = true;
10184  }
10185  else if(Train.BackgroundColour == clSignalStopBackground)
10186  // use colour to distinguish from call-on
10187  {
10188  SignalStopWarning = true;
10189  }
10190  else if(Train.BackgroundColour == clBufferAttentionNeeded)
10191  // use colour to distinguish from ordinary buffer stop
10192  {
10193  BufferAttentionWarning = true;
10194  }
10195  if(Train.HasTrainGone())
10196  {
10197  AnsiString Loc = "";
10198  bool ElementFound = false;
10199  TTrackElement TE;
10200  if(Train.LagElement > -1)
10201  {
10202  TE = Track->TrackElementAt(531, Train.LagElement);
10203  ElementFound = true;
10204  }
10205  else if(Train.MidElement > -1)
10206  {
10207  TE = Track->TrackElementAt(779, Train.MidElement);
10208  ElementFound = true;
10209  }
10210  else if(Train.LeadElement > -1)
10211  {
10212  TE = Track->TrackElementAt(780, Train.LeadElement);
10213  ElementFound = true;
10214  }
10215  if(ElementFound)
10216  {
10217  if(TE.ActiveTrackElementName != "")
10218  {
10219  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10220  }
10221  else
10222  {
10223  Loc = "track element " + TE.ElementID;
10224  }
10225  }
10226  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
10227  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
10228  // need above first because may also have ActionVectorEntryPtr == "Fer"
10229  {
10230  Train.UnplotTrain(9);
10231  // added at v1.3.0 to reset signals after train removed from an autosigsroute
10233  {
10236  }
10237  // end of addition
10238  AllRoutes->RebuildRailwayFlag = true;
10239  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
10240  // correctly after a crash
10241  }
10242  else if(AVEntryPtr->Command == "Fer")
10243  {
10244  bool CorrectExit = false;
10245  if(!AVEntryPtr->ExitList.empty())
10246  {
10247  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
10248  {
10249  if(*ELIT == Train.LagElement)
10250  {
10251  CorrectExit = true;
10252  }
10253  }
10254  }
10255  if(CorrectExit)
10256  {
10257  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, "", AVEntryPtr->EventTime, AVEntryPtr->Warning);
10258  }
10259  else
10260  {
10261  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
10262  }
10263  }
10264  else
10265  {
10266  if(!AVEntryPtr->SignallerControl)
10267  {
10268  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
10269  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
10270  // -2 is marker for send messages for all remaining actions except Fer if present
10271  }
10272  else
10273  {
10274  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, "", TDateTime(0), false); // false for Warning
10275  }
10276  }
10277  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
10278  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
10279  Train.DeleteTrain(1);
10280  TrainVector.erase(TrainVector.begin() + x);
10281  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
10282  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
10283  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
10284  }
10285  }
10286  }
10287  else
10288  {
10289  // reset all flags in case last train removed with flag set
10290  CrashWarning = false;
10291  DerailWarning = false;
10292  SPADWarning = false;
10293  CallOnWarning = false;
10294  SignalStopWarning = false;
10295  BufferAttentionWarning = false;
10296  TrainFailedWarning = false;
10297  }
10298  // update OpTimeToActMultimap
10300  {
10302  // clears entries then adds values for running trains then for continuation entries
10304  //added for multiplayer for running trains only
10305  }
10306  Utilities->Clock2Stopped = ClockState;
10307  Utilities->CallLogPop(723);
10308 }
10309 
10310 // ---------------------------------------------------------------------------
10312 {
10313  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
10314  if(!TrainVector.empty())
10315  {
10316  for(int x = TrainVector.size() - 1; x >= 0; x--)
10317  {
10318  TrainVectorAt(50, x).DeleteTrain(2);
10319  }
10320  TrainVector.clear();
10321  }
10322  if(!TrainDataVector.empty())
10323  {
10324  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
10325  {
10326  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10327  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10328  {
10329  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
10330  TOD.RunningEntry = NotStarted;
10331  TOD.TrainID = -1;
10332  TOD.EventReported = NoEvent;
10333  }
10334  }
10335  }
10336  Display->GetOutputLog1()->Caption = "";
10337  Display->GetOutputLog2()->Caption = "";
10338  Display->GetOutputLog3()->Caption = "";
10339  Display->GetOutputLog4()->Caption = "";
10340  Display->GetOutputLog5()->Caption = "";
10341  Display->GetOutputLog6()->Caption = "";
10342  Display->GetOutputLog7()->Caption = "";
10343  Display->GetOutputLog8()->Caption = "";
10344  Display->GetOutputLog9()->Caption = "";
10345  Display->GetOutputLog10()->Caption = "";
10346  Utilities->CallLogPop(1352);
10347 }
10348 
10349 // ---------------------------------------------------------------------------
10350 
10352 {
10353  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
10354  if(!TrainVector.empty())
10355  {
10356  for(unsigned int x = 0; x < TrainVector.size(); x++)
10357  {
10358  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
10359  { //see Kevin Smith error information for details
10360  TrainVectorAt(51, x).PlotTrain(4, Disp);
10361  }
10362  }
10363  }
10364  Utilities->CallLogPop(724);
10365 }
10366 
10367 // ---------------------------------------------------------------------------
10368 
10369 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
10370 {
10371  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
10372  if(!TrainVector.empty())
10373  {
10374  for(unsigned int x = 0; x < TrainVector.size(); x++)
10375  {
10376  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
10377  }
10378  }
10379  Utilities->CallLogPop(1707);
10380 }
10381 
10382 // ---------------------------------------------------------------------------
10383 
10385 {
10386  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
10387  if(!TrainVector.empty())
10388  {
10389  for(unsigned int x = 0; x < TrainVector.size(); x++)
10390  {
10391  TrainVectorAt(52, x).UnplotTrain(10);
10392  }
10393  }
10395  Utilities->CallLogPop(725);
10396 }
10397 
10398 // ---------------------------------------------------------------------------
10399 
10400 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
10401  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
10402  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
10403 {
10404  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
10405  "," + AnsiString(Mass) + "," + ModeStr);
10406  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
10407  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
10408 
10409  int RearExitPos = -1;
10410 
10411  for(int x = 0; x < 4; x++)
10412  {
10413  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
10414  {
10415  RearExitPos = x;
10416  }
10417  }
10418  if(RearExitPos == -1)
10419  {
10420  throw Exception("Error, RearExit == -1 in AddTrain");
10421  }
10422  bool ReportFlag = true;
10423 
10424  // used to stop repeated messages from CheckStartAllowable when split failed
10425  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
10426  {
10427  ReportFlag = false;
10428  }
10429  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
10430  {
10431  // messages sent to performance log in CheckStartAllowable if ReportFlag true
10432  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
10433  Utilities->CallLogPop(938);
10434  return(false);
10435  }
10436  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
10437  TTrainMode TrainMode = NoMode;
10438 
10439  if(ModeStr == "Timetable")
10440  {
10441  TrainMode = Timetable;
10442  }
10443  // all else gives 'None', 'Signaller' set within program
10444 
10445  if(MaxRunningSpeed < 10)
10446  {
10447  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
10448  }
10449  if(SignallerSpeed < 10)
10450  {
10451  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
10452  }
10453  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
10454  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
10455 
10456  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
10457 
10458  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10459  // initialise here rather than in TTrain constructor as create trains
10460  // with Null TrainDataEntryPtr when loading session trains
10461  if(SignallerControl)
10462  {
10463  NewTrain->TimetableFinished = true;
10464  NewTrain->SignallerStoppingFlag = false;
10465  NewTrain->TrainMode = Signaller;
10466  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10467  {
10468  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10469  }
10471  }
10472  // deal with starting conditions:-
10473  // unlocated Snt: just report entry & advance pointer
10474  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10475  // Sns doesn't need a new train
10476  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10477  // covers all above located starts
10478  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10479  // wouldn't have accepted the timetable
10480  {
10481  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10482  // StoppedAtBuffers is set in UpdateTrain()
10483  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10484  // buffer end must be ahead of train or would have failed start position check
10485  {
10486  NewTrain->StoppedAtLocation = true;
10487  NewTrain->PlotStartPosition(0);
10489  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10490  NewTrain->ActionVectorEntryPtr->Warning);
10491  if(!SignallerControl) // don't advance if SignalControlEntry
10492  {
10493  NewTrain->ActionVectorEntryPtr++;
10494  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10495  }
10496  NewTrain->LastActionTime = TTClockTime;
10497  }
10498  // else a through station stop
10499  else
10500  {
10501  NewTrain->StoppedAtLocation = true;
10502  NewTrain->PlotStartPosition(10);
10504  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10505  NewTrain->ActionVectorEntryPtr->Warning);
10506  if(!SignallerControl) // don't advance if SignalControlEntry
10507  {
10508  NewTrain->ActionVectorEntryPtr++;
10509  }
10510  NewTrain->LastActionTime = TTClockTime;
10511  }
10512  }
10513  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10514  {
10515  NewTrain->PlotStartPosition(11);
10516  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10517  AnsiString Loc = "";
10518  if(TE.ActiveTrackElementName != "")
10519  {
10520  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10521  }
10522  else
10523  {
10524  Loc = "track element " + TE.ElementID;
10525  }
10526  if(TE.TrackType == Continuation)
10527  {
10528  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10529  }
10530  else
10531  {
10532  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10533  }
10534  if(!SignallerControl) // don't advance if SignalControlEntry
10535  {
10536  NewTrain->ActionVectorEntryPtr++;
10537  }
10538  NewTrain->LastActionTime = TTClockTime;
10539  // no need to set LastActionTime for an unlocated entry
10540  }
10541  // cancel a wrong-direction route if either element of train starts on one
10542  if(NewTrain->LeadElement > -1)
10543  {
10544  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10545  }
10546  if(NewTrain->MidElement > -1)
10547  {
10548  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10549  }
10550  // set signals for a right-direction autosigs route for either element of train on one
10551  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10552  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10553  int RouteNumber = -1;
10554  bool SignalsSet = false;
10555 
10556  if(NewTrain->LeadElement > -1)
10557  {
10558  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10559  {
10560  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10561  int RouteStartPosition;
10562  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10563  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10564  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10565  if(FirstPair.first == RouteNumber)
10566  {
10567  RouteStartPosition = FirstPair.second;
10568  }
10569  else if(SecondPair.first == RouteNumber)
10570  {
10571  RouteStartPosition = SecondPair.second;
10572  }
10573  else
10574  {
10575  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10576  }
10577  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10578  SignalsSet = true;
10579  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10580  }
10581  else if(RouteNumber > -1) // non-autosigsroute
10582  {
10583  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10584  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10585  int FirstELinkPos = TempPDE.GetELinkPos();
10586  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10587  {
10588  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10589  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10590  }
10591  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10592  {
10593  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10594  // remove the last element under LeadElement
10595  }
10596  AllRoutes->RebuildRailwayFlag = true;
10597  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10598  // now deal with a rear linked autosigs route
10599  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10600  {
10601  int LinkedRouteNumber = -1;
10602  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10603  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10604  {
10605  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10606  // this is ok as here we are setting signals from the start of the route
10607  }
10608  }
10609  SignalsSet = true;
10610  }
10611  }
10612  if(NewTrain->MidElement > -1)
10613  // if entering at a continuation MidElement == -1
10614  {
10615  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10616  if(!SignalsSet)
10617  {
10618  RouteNumber = -1;
10619  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10620  {
10621  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10622  int RouteStartPosition;
10623  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10624  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10625  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10626  if(FirstPair.first == RouteNumber)
10627  {
10628  RouteStartPosition = FirstPair.second;
10629  }
10630  else if(SecondPair.first == RouteNumber)
10631  {
10632  RouteStartPosition = SecondPair.second;
10633  }
10634  else
10635  {
10636  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10637  }
10638  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10639  SignalsSet = true;
10640  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10641  }
10642  else if(RouteNumber > -1) // non-autosigsroute
10643  {
10644  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10645  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10646  int FirstELinkPos = TempPDE.GetELinkPos();
10647  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10648  {
10649  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10650  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10651  }
10652  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10653  {
10654  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10655  // remove the last element under LeadElement
10656  }
10657  AllRoutes->RebuildRailwayFlag = true;
10658  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10659  // now deal with a rear linked autosigs route
10660  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10661  {
10662  int LinkedRouteNumber = -1;
10663  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10664  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10665  {
10666  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10667  // this is ok as now we are setting signals from the start of the route
10668  }
10669  }
10670  }
10671  }
10672  }
10673  TrainVector.push_back(*NewTrain);
10674  Utilities->CallLogPop(731);
10675  return(true);
10676 }
10677 
10678 // ---------------------------------------------------------------------------
10679 
10680 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10681 {
10682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10683  AnsiString(TrackVectorNumber));
10684  int VecPos = -1;
10685 
10686  for(unsigned int x = 0; x < TrainVector.size(); x++)
10687  {
10688  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10689  {
10690  VecPos = x;
10691  }
10692  }
10693  if(VecPos == -1)
10694  {
10695  throw Exception("Error, VecPos not set in EntryPos");
10696  }
10697  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10698  {
10699  Utilities->CallLogPop(734);
10700  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10701  }
10702  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10703  {
10704  Utilities->CallLogPop(735);
10705  return(TrainVectorAt(5, VecPos).MidEntryPos);
10706  }
10707  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10708  {
10709  Utilities->CallLogPop(736);
10710  return(TrainVectorAt(7, VecPos).LagEntryPos);
10711  }
10712  Utilities->CallLogPop(737);
10713  return(-1);
10714 }
10715 
10716 // ---------------------------------------------------------------------------
10717 
10719 {
10720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10721  for(unsigned int x = 0; x < TrainVector.size(); x++)
10722  {
10723  if(TrainVectorAt(53, x).TrainID == TrainID)
10724  {
10725  Utilities->CallLogPop(738);
10726  return(TrainVectorAt(54, x));
10727  }
10728  }
10729  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10730 }
10731 
10732 // ---------------------------------------------------------------------------
10733 
10734 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10735 // return true if find the train (added at v2.4.0 as can select a removed train in
10736 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10737 {
10738  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10739  for(unsigned int x = 0; x < TrainVector.size(); x++)
10740  {
10741  if(TrainVectorAt(69, x).TrainID == TrainID)
10742  {
10743  Utilities->CallLogPop(2152);
10744  return(true);
10745  }
10746  }
10747  Utilities->CallLogPop(2153);
10748  return(false);
10749 }
10750 
10751 // ---------------------------------------------------------------------------
10752 
10753 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10754 {
10755  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10756  Utilities->Format96HHMMSS(Time));
10757  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10758 
10759  Utilities->CallLogPop(2061);
10760  return(RepeatTime);
10761 }
10762 
10763 // ---------------------------------------------------------------------------
10764 
10765 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10766 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10767 {
10768  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10769  AnsiString RetStr = "", PartStr = "";
10770  int Count = 0;
10771  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10772 
10773  Ptr--; // because incremented at start of loop
10774  do
10775  {
10776  Ptr++;
10777  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10778  {
10779  continue; // move past the starting entry
10780  }
10781  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10782  {
10783  break;
10784  }
10785  if(Ptr->SignallerControl)
10786  {
10787  RetStr = "Train under signaller control";
10788  break;
10789  }
10790  if(Ptr->FormatType == TimeTimeLoc)
10791  {
10792  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10793  {
10794  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10795  }
10796  else
10797  {
10798  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10799  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10800  Count++; // because there are 2 entries
10801  }
10802  }
10803  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10804  {
10805  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10806  }
10807  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10808  {
10809  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10810  }
10811  else if(Ptr->FormatType == PassTime) // new
10812  {
10813  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10814  }
10815  else if(Ptr->Command == "Fns")
10816  {
10817  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10818  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10819  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10820  }
10821  else if(Ptr->Command == "F-nshs")
10822  {
10823  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10824  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10825  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10826  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10827  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10828  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10829  }
10830 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10831  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10832  {
10833  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10834  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10835  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10836  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10837  }
10838  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10839  {
10840  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10841  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10842  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10843  }
10844  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10845  {
10846  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10847  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10848  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10849  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10850  }
10851  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10852  {
10853  PartStr = "Terminate at " + Ptr->LocationName;
10854  }
10855  else if(Ptr->Command == "Frh")
10856  {
10857  PartStr = "Terminate at " + Ptr->LocationName;
10858  }
10859  else if(Ptr->Command == "Fer")
10860  {
10861  AnsiString AllowedExits;
10862  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10863  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10864  }
10865  else if(Ptr->Command == "Fjo")
10866  {
10867  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10868  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10869  }
10870  else if(Ptr->Command == "jbo")
10871  {
10872  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10873  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10874  }
10875  else if(Ptr->Command == "fsp")
10876  {
10877  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10878  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10879  }
10880  else if(Ptr->Command == "rsp")
10881  {
10882  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10883  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10884  }
10885  else if(Ptr->Command == "cdt")
10886  {
10887  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10888  }
10889  else if(Ptr->Command == "dsc")
10890  {
10891  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(27, Ptr->EventTime, RepNum, IncMins)) + ": Change description at " + Ptr->LocationName;
10892  }
10893  if(RetStr != "")
10894  {
10895  RetStr = RetStr + '\n' + PartStr;
10896  }
10897  else
10898  {
10899  RetStr = PartStr;
10900  }
10901  Count++;
10902  }
10903  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10904  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10905  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10906  // forward as anyone should wish to see without looking at the full timetable
10907  Utilities->CallLogPop(2072);
10908  return(RetStr);
10909 }
10910 
10911 // ---------------------------------------------------------------------------
10912 
10913 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10914 { //no delays as train not entered yet
10915  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - TDEPtr->ActionVector.begin()) + ","
10916  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10917  AnsiString DepTime = "", EventTime = "";
10918  bool CDTFlag = false; //reports if train changes direction before departs
10919  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10920  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10921  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10922  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10923  {
10924  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10925  {
10926  TowardsLocation = AVI->LocationName;
10927  }
10928  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10929  {
10930  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10931  if(TE.ActiveTrackElementName != "")
10932  {
10933  TowardsLocation = TE.ActiveTrackElementName;
10934  }
10935  else
10936  {
10937  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10938  }
10939  }
10940  }
10941  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10942  {
10943  if(AVI->Command == "cdt")
10944  {
10945  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10946  continue;
10947  }
10948  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10949  {
10950  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10951  RetStr += "\nNew service splits at " + EventTime;
10952  Utilities->CallLogPop(2237);
10953  return(RetStr);
10954  }
10955  if(AVI->Command == "jbo")
10956  {
10957  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(30, AVI->EventTime, RptNum, IncrementalMinutes));
10958  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10959  Utilities->CallLogPop(2238);
10960  return(RetStr);
10961  }
10962  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh"))
10963  {
10964  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(31, AVI->EventTime, RptNum, IncrementalMinutes));
10965  RetStr += "\nNew service finishes and forms another new service at " + EventTime;
10966  Utilities->CallLogPop(2607);
10967  return(RetStr);
10968  }
10969  if(AVI->Command == "Fjo")
10970  {
10971  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10972  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at " + EventTime;
10973  Utilities->CallLogPop(2608);
10974  return(RetStr);
10975  }
10976  if(AVI->Command == "Frh")
10977  {
10978  RetStr += "\nNew service finishes and remains at location.";
10979  Utilities->CallLogPop(2609);
10980  return(RetStr);
10981  }
10982  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10983  {
10984  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10985  if(CDTFlag)
10986  {
10987  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10988  {
10989  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10990  }
10991  else
10992  {
10993  RetStr += "\nNew service changes direction then departs at " + DepTime;
10994  }
10995  }
10996  else
10997  {
10998  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10999  {
11000  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
11001  }
11002  else
11003  {
11004  RetStr += "\nNew service departs at " + DepTime;
11005  }
11006  }
11007  Utilities->CallLogPop(2239);
11008  return(RetStr);
11009  }
11010  }
11011  Utilities->CallLogPop(2223);
11012  return(RetStr);
11013 }
11014 
11015 // ---------------------------------------------------------------------------
11016 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
11017 /*
11018  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
11019  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
11020  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
11021 
11022  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
11023  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
11024  user wishes
11025 
11026  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11027  descriptive text or anything user wishes
11028  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11029  be ignored) is taken as the timetable start time.
11030  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11031  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11032  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11033  within the timetable if required.
11034  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11035  services)
11036  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11037  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11038 
11039  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11040  text timetable file easier
11041 
11042  form:-
11043  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11044  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11045  then multiple entries, separated by commas, of the form:-
11046 
11047  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11048  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
11049  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
11050 
11051  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
11052  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
11053  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
11054 
11055  HH:MM;Command (cdt) }TimeCmd }
11056  HH:MM;Command;Description (dsc) }TimeCmdDescription }
11057  HH:MM;Location (arr & dep) }TimeLoc }
11058  HH:MM;HH:MM;Location }TimeTimeLoc }
11059  HH:MM;pas;Location }PassTime }
11060  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
11061  HH:MM;Fer;set of allowable IDs }ExitRailway }
11062  Command (Frh only) }FinRemHere }
11063 
11064  R;mm;dd;nn. Repeat Repeat entry
11065 
11066  Formats:
11067 
11068  Command only: Frh
11069  Time;Command: cdt
11070  Time;Command;Description dsc
11071  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
11072  Time;Command;2 Element IDs: Snt
11073  Time;Comand;n Element IDs: Fer
11074  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
11075  Time;Command;2 Element IDs;Headcode Snt-sh
11076  Time;Command;Location pas
11077  Time;Location Arr Dep
11078  Time;Time;Location Arr & dep together
11079 
11080  10 Non-linked entries: Snt (located or unlocated); pas; cdt; dsc; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
11081 
11082  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
11083  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
11084  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
11085 
11086  4 2x Linked entries, all shuttles:
11087 
11088  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
11089  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
11090  -> Remain Here (at finish location after all repeats)
11091  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
11092  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
11093 
11094  Moving/AtLoc states:-
11095 
11096  Successor state Type
11097 
11098  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
11099  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
11100  Sfs AtLoc )
11101  Sns AtLoc ) Start
11102  Sns-fsh AtLoc )
11103  Snt-sh AtLoc )
11104  Sns-sh AtLoc )
11105 
11106  pas Moving )
11107  jbo AtLoc )
11108  fsp AtLoc )
11109  rsp AtLoc ) Intermediate
11110  cdt AtLoc )
11111  dsc AtLoc )
11112  TimeLoc arr Moving (bef), AtLoc (aft) )
11113  TimeLoc dep AtLoc (bef), Moving (aft) )
11114  TimeTimeLoc Moving )
11115 
11116  Fns Repeat/Nothing)
11117  Fjo Repeat/Nothing)
11118  Frh Repeat/Nothing)
11119  Fer Repeat/Nothing) Finish
11120  Frh-sh Repeat )
11121  Fns-sh Repeat )
11122  F-nshs Nothing )
11123 
11124  Descriptions:
11125  Snt New train
11126  Sfs New service from split
11127  Sns New service from another service
11128  Sns-fsh New non-repeating service from a shuttle service
11129  Snt-sh New shuttle train at a timetabled stop
11130  Sns-sh New shuttle service from a feeder service
11131 
11132  pas Pass
11133  jbo Be joined by another train
11134  fsp Front split
11135  rsp Rear split
11136  cdt Change direction of train
11137  dsc Change description of train
11138  TimeLoc arr Arrival
11139  TimeLoc dep Departure
11140  TimeTimeLoc Arrival and departure
11141 
11142  Fns Finish & form a new service
11143  Fjo Finish & join another train
11144  Frh Finish & remain here
11145  Fer Finish & exit railway
11146  Frh-sh Finish & repeat shuttle, finally remain here
11147  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
11148  F-nshs Finish & form a shuttle feeder service
11149 */
11150 
11151 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
11152 {
11153  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
11154  // a line that is too long; timetable containing too few lines; and timetable failed to open.
11155  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
11156  // new for v0.2b
11157  // compile ActiveTrackElementNameMap
11158  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
11159 
11161  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
11162  {
11163  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
11165  == Track->ContinuationNameMap.end())
11166  {
11167  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
11168  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
11169  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
11170  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
11171  }
11172  }
11174  // end of new section
11175  std::ifstream TTBLFile(FileName, std::ios_base::binary);
11176 
11177  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
11178  if(TTBLFile.is_open())
11179  {
11180  char *TrainTimetableString = new char[10000];
11181  // enough for over 200 stations, should be adequate!
11182  bool EndOfFile = false;
11183  int Count = 0;
11184  // counts 'relevant' lines, i.e ignores any before the start time on its own line
11185  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11186  // delimiter is '\0' as it's an AnsiString
11187  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11188  // file empty - stores a null in 1st position if doesn't load any characters
11189  {
11190  // may still have eof even if read a line (no CRLF at end), and
11191  // if so need to process it
11192  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
11193  TTBLFile.close();
11194  delete[] TrainTimetableString;
11195  Utilities->CallLogPop(1611);
11196  return(false);
11197  }
11198  AnsiString OneLine(TrainTimetableString);
11199  bool FinalCallFalse = false;
11200  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
11201  // get rid of lines before the start time
11202  {
11203  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
11204  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11205  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11206  // stores a null in 1st position if doesn't load any characters
11207  {
11208  // may still have eof even if read a line (no CRLF at end), and
11209  // if so need to process it
11210  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
11211  TTBLFile.close();
11212  delete[] TrainTimetableString;
11213  Utilities->CallLogPop(772);
11214  return(false);
11215  }
11216  OneLine = AnsiString(TrainTimetableString);
11217  }
11218  // here when have accepted the start time
11219  Count++; // increment past the start time
11220  while(!EndOfFile)
11221  {
11222  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11223  // get next line after start time
11224  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11225  // stores a null in 1st position if doesn't load any characters
11226  {
11227  // may still have eof even if read a line (no CRLF at end), and
11228  // if so need to process it
11229  EndOfFile = true;
11230  OneLine = "";
11231  }
11232  else
11233  {
11234  OneLine = AnsiString(TrainTimetableString);
11235  }
11236  if(OneLine.Length() > 9999)
11237  {
11238  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
11239  TTBLFile.close();
11240  delete[] TrainTimetableString;
11241  Utilities->CallLogPop(789);
11242  return(false);
11243  }
11244  bool FinalCallFalse = false;
11245  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
11246  // false for FinalCall - just checking at this stage
11247  {
11248  TTBLFile.close();
11249  delete[] TrainTimetableString;
11250  Utilities->CallLogPop(770);
11251  return(false);
11252  }
11253  if(EndOfFile && (Count < 2))
11254  // Timetable must contain at least two relevant lines, one for start time and at least one train
11255  {
11256  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
11257  TTBLFile.close();
11258  delete[] TrainTimetableString;
11259  Utilities->CallLogPop(771);
11260  return(false);
11261  }
11262  Count++;
11263  }
11264  delete[] TrainTimetableString;
11265  TTBLFile.close();
11266  } // if(TTBLFile.is_open())
11267  else
11268  {
11269  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
11270  Utilities->CallLogPop(2154);
11271  return(false);
11272  }
11273  Utilities->CallLogPop(753);
11274  return(true);
11275 }
11276 
11277 // ---------------------------------------------------------------------------
11278 
11279 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
11280  bool CheckLocationsExistInRailway) // return true for success
11281 
11282 /* Format:
11283  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11284  descriptive text or anything user wishes
11285  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11286  be ignored) is taken as the timetable start time.
11287  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11288  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11289  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11290  within the timetable if required.
11291  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11292  services)
11293  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11294  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11295 
11296  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11297  text timetable file easier
11298 
11299  form:-
11300  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11301  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11302  then multiple entries, separated by commas, of the form:-
11303 
11304  Format FormatType
11305  [W]HH:MM;Command (cdt) }TimeCmd }
11306  [W]HH:MM;dsc;new description }TimeCmdDescription }
11307  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
11308  [W]HH:MM;pas;Location }PassTime }
11309  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11310  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
11311  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
11312  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
11313  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
11314  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
11315  [W]HH:MM;Fns-sh;Details }FSHNewService }
11316  [W]HH:MM;Location (arr & dep) }TimeLoc }
11317  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
11318  Command (Frh only) }FinRemHere }
11319 
11320  R;mm;dd;nn. Repeat Repeat entry
11321 
11322  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
11323  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
11324  at location; or (c) departure time if train already at location (including train started at location either as a new
11325  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
11326  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
11327  minutes, incremental train headcode last 2 digits, and number of repeats.
11328 
11329  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
11330  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
11331  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
11332  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
11333  (it's for a shuttle train to return to depot at end of services)
11334 
11335  Command/Location & details are as follows:-
11336 
11337  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
11338  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
11339  2E44 in its Sfs entry. All these are checked.
11340  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
11341 
11342  Start commands:-
11343  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
11344  with loc as a start entry can't have a location as details)
11345  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
11346  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
11347  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
11348  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
11349  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
11350  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
11351  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
11352  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
11353  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
11354  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
11355  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
11356 
11357  Intermediate commands:-
11358  Time - Location (TimeLoc), can be arrival or departure depending on context
11359  Time Time location (TimeTimeLoc), arrival and departure
11360  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
11361  pas (PassTime), Time;pas;Location
11362  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
11363  joining train's finish details must correspond or the file check will fail
11364  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
11365  new train - that train's starting information must correspond)
11366  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
11367  new train - that train's starting information must correspond)
11368  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
11369  dsc (TimeCmdDescription) = Change description of train = new description only, no train creation
11370 
11371  Finish commands:-
11372  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
11373  creation)
11374  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
11375  shuttle headcode (no train creation)
11376  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
11377  may have to wait for it), details = new headcode (delete train)
11378  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
11379  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
11380  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
11381  here
11382  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
11383  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
11384 
11385  Repeat:-
11386  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
11387  headcodes - it is up to user to avoid duplicates if he/she wishes to.
11388 
11389  Checks carried out with error messages in this function:-
11390  At least one comma in a service line (it's based on a .csv file)
11391  No entries following train information;
11392  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11393  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11394  First entry not a start entry;
11395  Train information incomplete before a start entry;
11396  Entry follows a finish entry but doesn't begin with 'R';
11397  SplitEntry returns false in a finish entry - message repeats the entry for information;
11398  Last action entry isn't a finish entry.
11399 
11400  Function returns false with no message if:-
11401  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11402  time is found at all then an error message is given in the calling function);
11403  SplitTrainInfo returns false (message given in called function);
11404  SplitRepeat returns false (message given in called function).
11405 */
11406 {
11407  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
11408  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
11409  TTrainDataEntry TempTrainDataEntry;
11410 
11411  EndOfFile = false;
11412  StripSpaces(0, OneLine);
11413  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
11414  // semicolons within the line
11415  ServiceReference = "";
11416  if(OneLine != "")
11417  {
11418  if(OneLine[1] != '*')
11419  {
11420  int SCPos = OneLine.Pos(';');
11421  if(SCPos == 0)
11422  {
11423  ServiceReference = OneLine.SubString(1, 8);
11424  }
11425  else
11426  {
11427  ServiceReference = OneLine.SubString(1, (SCPos - 1));
11428  }
11429  }
11430  }
11431  bool AllCommas = true;
11432 
11433  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
11434  {
11435  if(OneLine[x] != ',')
11436  {
11437  AllCommas = false;
11438  }
11439  }
11440  if(AllCommas || (OneLine == ""))
11441  {
11442  if(Count > 0)
11443  {
11444  EndOfFile = true;
11445  // returns true for a blank line (or a line of all commas) - treated as end of file
11446  Utilities->CallLogPop(1018);
11447  return(true);
11448  }
11449  else // count == 0 so not yet found a start time, no message to be given
11450  {
11451  Utilities->CallLogPop(754);
11452  return(false);
11453  }
11454  }
11455  AnsiString First = "", Second = "", Third = "", Fourth = "";
11456  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
11457  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
11458  TDateTime StartTime(0);
11459  TNumList ExitList;
11460  bool Warning = false;
11461 
11462  if(Count == 0) // no start time found yet
11463  {
11464 /* dropped at v0.6b
11465  AnyHeadCodeValid = false;
11466  if(OneLine.SubString(6,5) == ";0000")
11467  {
11468  AnyHeadCodeValid = true;
11469  }
11470 */
11471  if(!CheckTimeValidity(0, OneLine, StartTime))
11472  {
11473  // no message is given for an invalid time as it's assumed to be an irrelevant line
11474  // if no start time is found at all then an error message is given in the calling function
11475  // AnyHeadCodeValid = false;
11476  Utilities->CallLogPop(755);
11477  return(false);
11478  }
11479  if(FinalCall) // here if start time valid
11480  {
11481  TTClockTime = StartTime;
11482  TimetableStartTime = StartTime;
11483  }
11484  }
11485  else
11486  {
11487  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
11488  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11489  double MaxBrakeRate = 0;
11490  double PowerAtRail = 0;
11491  int SignallerSpeed = 0;
11492  if(OneLine[1] == '*')
11493  {
11494  Utilities->CallLogPop(1581);
11495  return(true);
11496  // ignore any line beginning with '*' but return true as there is no error
11497  }
11498  int Pos = OneLine.Pos(',');
11499  if(Pos == 0)
11500  {
11501  int SubStringLength = 20;
11502  if(OneLine.Length() < 20)
11503  {
11504  SubStringLength = OneLine.Length();
11505  }
11506  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11507  Utilities->CallLogPop(766);
11508  return(false);
11509  }
11510  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11511  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11512  GiveMessages)) // error messages given in SplitTrainInfo
11513  {
11514  Utilities->CallLogPop(773);
11515  return(false);
11516  }
11517  if(FinalCall)
11518  {
11519  // store Train info - conversions done in SplitTrainInfo
11520  // only headcode mandatory for continuing services
11521  //HeadCode = ServiceReference until final section of SecondPassActions
11522  TempTrainDataEntry.HeadCode = HeadCode;
11523  TempTrainDataEntry.ServiceReference = HeadCode;
11524  TempTrainDataEntry.FixedDescription = Description; //name changed at v2.16.1
11525  TempTrainDataEntry.ExplicitDescription = false;
11526  if(Description != "")
11527  {
11528  TempTrainDataEntry.ExplicitDescription = true;
11529  }
11530  TempTrainDataEntry.StartSpeed = StartSpeed;
11531  TempTrainDataEntry.Mass = Mass;
11532  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11533  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11534  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11535  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11536  TTrainOperatingData TempTrainOperatingData;
11537  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11538  }
11539  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11540  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11541  // so strip them off
11542  while(NewRemainder[NewRemainder.Length()] == ',')
11543  {
11544  if(NewRemainder.Length() > 1)
11545  {
11546  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11547  }
11548  else
11549  {
11550  NewRemainder = "";
11551  break;
11552  }
11553  }
11554  // check if zero length & fail if so
11555  if(NewRemainder == "")
11556  {
11557  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11558  Utilities->CallLogPop(769);
11559  return(false);
11560  }
11561  // now have one more entry than there are commas
11562  int CommaCount = 0;
11563  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11564  {
11565  if(NewRemainder[x] == ',')
11566  {
11567  CommaCount++;
11568  }
11569  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11570  if(CommaCount == 0)
11571  {
11572  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11573  {
11574  int SubStringLength = 20;
11575  if(OneLine.Length() < 20)
11576  {
11577  SubStringLength = OneLine.Length();
11578  }
11579  TimetableMessage(GiveMessages,
11580  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11581  OneLine.SubString(1, SubStringLength) + "'....");
11582  Utilities->CallLogPop(783);
11583  return(false);
11584  }
11585  }
11586  AnsiString OneEntry = "";
11587  TTimetableFormatType FormatType;
11588  TTimetableSequenceType SequenceType;
11589  TTimetableLocationType LocationType;
11590  TTimetableShuttleLinkType ShuttleLinkType;
11591  bool FinishFlag = false;
11592  bool NewTrain = false;//added at v2.14.0 to record created trains for later zero power checks
11593  for(int x = 0; x < CommaCount + 1; x++)
11594  {
11595  if((CommaCount == 0) || (x < CommaCount))
11596  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11597  // in which case there's a comma & finish element or repeat still to come this entry could
11598  // be a finish but can't be a repeat
11599  {
11600  if(CommaCount == 0)
11601  {
11602  OneEntry = NewRemainder;
11603  NewRemainder = "";
11604  }
11605  else
11606  {
11607  Pos = NewRemainder.Pos(',');
11608  OneEntry = NewRemainder.SubString(1, Pos - 1);
11609  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11610  }
11611  First = "";
11612  Second = "";
11613  Third = "";
11614  Fourth = "";
11615  RearStartOrRepeatMins = 0;
11616  FrontStartOrRepeatDigits = 0;
11617  NumberOfRepeats = 0;
11618  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11619  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11620  {
11621  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11622  Utilities->CallLogPop(756);
11623  return(false);
11624  }
11625  if((Second == "Snt") || (Second == "Snt-sh")) //added at v2.14.0, see above
11626  {
11627  NewTrain = true;
11628  }
11629  // check if warning for Frh or Fjo & reject
11630  if(Warning && (Second == "Frh"))
11631  {
11632  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11633  Utilities->CallLogPop(1793);
11634  return(false);
11635  }
11636  if(Warning && (Second == "Fjo"))
11637  {
11638  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11639  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11640  Utilities->CallLogPop(1794);
11641  return(false);
11642  }
11643  //below added at v2.14.0 to prevent unpowered trains attempting to be joined by (Second == jbo), split (Second -- fsp or rsp),
11644  //or change direction. Form a new service dealt with below for zero power as it's a finish event.
11645  if(NewTrain && (PowerAtRail < 1) && (Second == "jbo"))
11646  {
11647  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11648  "': a train created without power can't 'be joined by' another train (i.e. can't include command 'jbo'), "
11649  "use command 'Fjo' (i.e. 'join' another train) instead immediately after the line containing 'Snt', and use "
11650  "command 'jbo' for the train it is to join.");
11651  Utilities->CallLogPop(2545);
11652  return(false);
11653  }
11654  if(NewTrain && (PowerAtRail < 1) && ((Second == "fsp") || (Second == "rsp")))
11655  {
11656  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11657  "': a train created without power can't split.");
11658  Utilities->CallLogPop(2546);
11659  return(false);
11660  }
11661  if(NewTrain && (PowerAtRail < 1) && (Second == "cdt"))
11662  {
11663  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11664  "': a train created without power can't change direction under timetable control.");
11665  Utilities->CallLogPop(2547);
11666  return(false);
11667  }
11668  //end of new additions
11669  if(x == 0) // should be start event
11670  {
11671  if(SequenceType != StartSequence)
11672  {
11673  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11674  Utilities->CallLogPop(784);
11675  return(false);
11676  }
11677  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11678  {
11679  if(NewRemainder[1] != 'R')
11680  {
11681  TimetableMessage(GiveMessages,
11682  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11683  OneEntry + "'");
11684  Utilities->CallLogPop(787);
11685  return(false);
11686  }
11687  }
11688  if((Second == "Snt") || (Second == "Snt-sh"))
11689  // need full train information including non-default values for at least HeadCode, Description,
11690  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11691  {
11692  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11693  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11694  {
11695  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11696  OneEntry + "'");
11697  Utilities->CallLogPop(1783);
11698  return(false);
11699  }
11700  }
11701  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11702  // service continuation - need at least non-default value for HeadCode
11703  {
11704  if(HeadCode == "")
11705  {
11706  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11707  OneEntry + "'");
11708  Utilities->CallLogPop(788);
11709  return(false);
11710  }
11711  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11712  {
11713  TimetableMessage(GiveMessages,
11714  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11715  OneEntry + "'");
11716  Utilities->CallLogPop(843);
11717  return(false);
11718  }
11719  }
11720  }
11721  if(SequenceType == FinishSequence)
11722  {
11723  FinishFlag = true;
11724  // marker for only permitted additional entry being a repeat, only needed if the
11725  // finish entry is not the last entry
11726  }
11727  if(FinalCall)
11728  {
11729  // interpret & add to ActionVector
11730  TDateTime TempTime;
11731  TActionVectorEntry ActionVectorEntry;
11732  ActionVectorEntry.FormatType = FormatType;
11733  ActionVectorEntry.LocationType = LocationType;
11734  ActionVectorEntry.SequenceType = SequenceType;
11735  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11736  ActionVectorEntry.Warning = Warning;
11737  if(FormatType == TimeLoc)
11738  {
11739  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11740  {
11741  ;
11742  } // these will all be true as final call
11743 
11744  ActionVectorEntry.LocationName = Second;
11745  }
11746  else if(FormatType == PassTime) // new
11747  {
11748  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11749  {
11750  ;
11751  }
11752  ActionVectorEntry.Command = Second;
11753  ActionVectorEntry.LocationName = Third;
11754  }
11755  else if(FormatType == TimeTimeLoc)
11756  {
11757  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11758  {
11759  ;
11760  }
11761  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11762  {
11763  ;
11764  }
11765  ActionVectorEntry.LocationName = Third;
11766  }
11767  else if(FormatType == TimeCmd)
11768  {
11769  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11770  {
11771  ;
11772  }
11773  ActionVectorEntry.Command = Second;
11774  }
11775  else if(FormatType == ExitRailway)
11776  {
11777  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11778  {
11779  ;
11780  }
11781  ActionVectorEntry.Command = Second;
11782  ActionVectorEntry.ExitList = ExitList;
11783  }
11784  else if(FormatType == StartNew)
11785  {
11786  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11787  {
11788  ;
11789  }
11790  ActionVectorEntry.Command = Second;
11791  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11792  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11793  if(Fourth == 'S')
11794  {
11795  ActionVectorEntry.SignallerControl = true;
11796  }
11797  }
11798  else if(FormatType == SNTShuttle)
11799  {
11800  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11801  {
11802  ;
11803  }
11804  ActionVectorEntry.Command = Second;
11805  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11806  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11807  ActionVectorEntry.OtherHeadCode = Fourth;
11808  }
11809  else if(FormatType == SNSShuttle)
11810  {
11811  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11812  {
11813  ;
11814  }
11815  ActionVectorEntry.Command = Second;
11816  ActionVectorEntry.OtherHeadCode = Third;
11817  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11818  }
11819  else if(FormatType == TimeCmdHeadCode) //fsp/rsp is TimeCmdHeadCode but may have a Fourth - see below
11820  {
11821  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11822  {
11823  ;
11824  }
11825  ActionVectorEntry.Command = Second;
11826  ActionVectorEntry.OtherHeadCode = Third;
11827  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //new at v2.15.0 & checked in SplitEntry
11828  {
11829  ActionVectorEntry.SplitDistribution = Fourth;
11830  }
11831  }
11832  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11833  {
11834  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11835  {
11836  ;
11837  }
11838  ActionVectorEntry.Command = Second;
11839  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11840  }
11841  else if(FormatType == FSHNewService)
11842  {
11843  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11844  {
11845  ;
11846  }
11847  ActionVectorEntry.Command = Second;
11848  ActionVectorEntry.OtherHeadCode = Third;
11849  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11850  }
11851  else if(FormatType == FinRemHere)
11852  {
11853  ActionVectorEntry.Command = Second;
11854  }
11855  else if(FormatType == TimeCmdDescription) //new at v2.15.0
11856  {
11857  if(CheckTimeValidity(35, First, ActionVectorEntry.EventTime))
11858  {
11859  ;
11860  }
11861  ActionVectorEntry.Command = Second;
11862  ActionVectorEntry.NewDescription = Third;
11863  }
11864  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11865  }
11866  }
11867  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11868  {
11869  OneEntry = NewRemainder;
11870  First = "";
11871  Second = "";
11872  Third = "";
11873  Fourth = "";
11874  RearStartOrRepeatMins = 0;
11875  FrontStartOrRepeatDigits = 0;
11876  NumberOfRepeats = 0;
11877  if((FinishFlag) && (OneEntry[1] != 'R'))
11878  // already had a finish entry
11879  {
11880  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11881  Utilities->CallLogPop(79);
11882  return(false);
11883  }
11884  if(OneEntry[1] != 'R') // must be finish
11885  {
11886  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11887  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11888  {
11889  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11890  Utilities->CallLogPop(757);
11891  return(false);
11892  }
11893  //below added at v2.14.0 to prevent unpowered trains attempting to form a new service.
11894  if(NewTrain && (PowerAtRail < 1) && ((Second == "Fns") || (Second == "Frh-sh") || (Second == "Fns-sh") || (Second == "F-nshs")))
11895  {
11896  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11897  "': a train created without power can't form a new service.");
11898  Utilities->CallLogPop(2548);
11899  return(false);
11900  }
11901  //end of new additions
11902  if(SequenceType != FinishSequence)
11903  {
11904  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11905  Utilities->CallLogPop(785);
11906  return(false);
11907  }
11908  if(FinalCall)
11909  {
11910  // interpret & add to ActionVector
11911  TDateTime TempTime;
11912  TActionVectorEntry ActionVectorEntry;
11913  ActionVectorEntry.FormatType = FormatType;
11914  ActionVectorEntry.LocationType = LocationType;
11915  ActionVectorEntry.SequenceType = SequenceType;
11916  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11917  ActionVectorEntry.Warning = Warning;
11918  if(FormatType == TimeCmd)
11919  {
11920  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11921  {
11922  ;
11923  }
11924  ActionVectorEntry.Command = Second;
11925  }
11926  else if(FormatType == TimeCmdHeadCode)
11927  {
11928  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11929  {
11930  ;
11931  }
11932  ActionVectorEntry.Command = Second;
11933  ActionVectorEntry.OtherHeadCode = Third;
11934  }
11935  else if(FormatType == FNSNonRepeatToShuttle)
11936  {
11937  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11938  {
11939  ;
11940  }
11941  ActionVectorEntry.Command = Second;
11942  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11943  }
11944  else if(FormatType == FSHNewService)
11945  {
11946  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11947  {
11948  ;
11949  }
11950  ActionVectorEntry.Command = Second;
11951  ActionVectorEntry.OtherHeadCode = Third;
11952  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11953  }
11954  else if(FormatType == ExitRailway)
11955  {
11956  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11957  {
11958  ;
11959  }
11960  ActionVectorEntry.Command = Second;
11961  ActionVectorEntry.ExitList = ExitList;
11962  }
11963  else if(FormatType == FinRemHere)
11964  {
11965  ActionVectorEntry.Command = Second;
11966  }
11967  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11968  }
11969  }
11970  else // repeat
11971  {
11972  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11973  {
11974  Utilities->CallLogPop(786);
11975  // error messages given in SplitRepeat
11976  return(false);
11977  }
11978  if(FinalCall)
11979  {
11980  TActionVectorEntry ActionVectorEntry;
11981  ActionVectorEntry.FormatType = Repeat;
11982  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11983  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11984  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11985  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11986  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11987  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11988  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11989  }
11990  }
11991  }
11992  }
11993  if(FinalCall)
11994  {
11995  TrainDataVector.push_back(TempTrainDataEntry);
11996  }
11997  }
11998  Utilities->CallLogPop(80);
11999  return(true);
12000 }
12001 
12002 // ---------------------------------------------------------------------------
12003 
12004 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
12005 {
12006  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
12007  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
12008  {
12009  Utilities->CallLogPop(1890);
12010  return(false);
12011  }
12012  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
12013  {
12014  Utilities->CallLogPop(1891);
12015  return(false);
12016  }
12017  Utilities->CallLogPop(1892);
12018  return(true);
12019 }
12020 
12021 // ---------------------------------------------------------------------------
12022 
12023 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
12024 // 1st 5 chars must be HH:MM, anything else will be ignored
12025 {
12026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
12027  if(TimeStr.Length() < 5)
12028  {
12029  Utilities->CallLogPop(926);
12030  return(false);
12031  }
12032  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
12033  {
12034  Utilities->CallLogPop(927);
12035  return(false);
12036  }
12037  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
12038  {
12039  Utilities->CallLogPop(928);
12040  return(false);
12041  }
12042  if(TimeStr[3] != ':')
12043  {
12044  Utilities->CallLogPop(929);
12045  return(false);
12046  }
12047  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
12048  {
12049  Utilities->CallLogPop(930);
12050  return(false);
12051  }
12052  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
12053  {
12054  Utilities->CallLogPop(931);
12055  return(false);
12056  }
12057  while(TimeStr.Length() > 5)
12058  {
12059  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
12060  }
12061  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
12062  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
12063 
12064  if((WholeHours + FracHour) >= 95.98334)
12065  {
12066  Utilities->CallLogPop(1817);
12067  return(false); // > 95h 59m
12068  }
12069  Time = TDateTime((WholeHours + FracHour) / 24);
12070  Utilities->CallLogPop(932);
12071  return(true);
12072 }
12073 
12074 // ---------------------------------------------------------------------------
12075 
12076 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
12077  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
12078  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
12079 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
12080  Return false for failure.
12081  See description above under ProcessOneTimetableLinefor details of train action entries
12082  NB all types set except LocationType for Snt as may be located or not
12083 */{
12084  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
12085  Warning = false;
12086  TDateTime TempTime;
12087 
12088  if(OneEntry.Length() > 0)
12089  {
12090  if(OneEntry[1] == 'W') // warning
12091  {
12092  Warning = true;
12093  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
12094  // strip it off
12095  }
12096  }
12097  if(OneEntry == "Frh")
12098  {
12099  FormatType = FinRemHere;
12100  SequenceType = FinishSequence;
12101  LocationType = AtLocation;
12102  ShuttleLinkType = NotAShuttleLink;
12103  Second = "Frh";
12104  Utilities->CallLogPop(1016);
12105  return(true);
12106  }
12107  if(OneEntry.Length() < 7)
12108  {
12109  Utilities->CallLogPop(907);
12110  return(false); // 'HH:MM;' + at least a one-letter location name
12111  }
12112  int Pos = OneEntry.Pos(';'); // first segment delimiter
12113 
12114  if(Pos != 6)
12115  {
12116  Utilities->CallLogPop(908);
12117  return(false);
12118  // no delimiter or delimiter not in position 6, has to be a time so fail
12119  }
12120  First = OneEntry.SubString(1, 5); // has to be a time
12121  if(!CheckTimeValidity(16, First, TempTime))
12122  {
12123  Utilities->CallLogPop(909);
12124  return(false);
12125  }
12126  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
12127 
12128 // if((Remainder[1] >= '0') && (Remainder[1] <= '9')) changed at v2.16.0 so only 'digit-digit-colon....' interpreted as a time - to allow locations to begin with digits
12129  if((Remainder.Length() >= 3) && (Remainder[1] >= '0') && (Remainder[1] <= '9') && (Remainder[2] >= '0') && (Remainder[2] <= '9') && (Remainder[3] == ':'))
12130  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
12131  {
12132  if(Remainder.Length() < 7)
12133  {
12134  Utilities->CallLogPop(910);
12135  return(false); // 'HH:MM;' + at least a one-letter location name
12136  }
12137  Pos = Remainder.Pos(';'); // second segment delimiter
12138  if(Pos == 0)
12139  {
12140  Utilities->CallLogPop(911);
12141  return(false);
12142  // no delimiter, has to be one between departure time & location
12143  }
12144  Second = Remainder.SubString(1, 5); // has to be a time
12145  if(!CheckTimeValidity(15, Second, TempTime))
12146  {
12147  Utilities->CallLogPop(912);
12148  return(false);
12149  }
12150  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12151  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
12152  {
12153  Utilities->CallLogPop(913);
12154  return(false);
12155  }
12156  FormatType = TimeTimeLoc;
12157  SequenceType = IntermediateSequence;
12158  LocationType = AtLocation;
12159  ShuttleLinkType = NotAShuttleLink;
12160  Utilities->CallLogPop(914);
12161  return(true);
12162  }
12163  Pos = Remainder.Pos(';'); // second segment delimiter
12164  if(Pos == 0) // no third segment so second must be a location, or cdt
12165  {
12166  Second = Remainder;
12167  if(Second == "cdt")
12168  {
12169  FormatType = TimeCmd;
12170  ShuttleLinkType = NotAShuttleLink;
12171  LocationType = AtLocation;
12172  SequenceType = IntermediateSequence;
12173  Utilities->CallLogPop(915);
12174  return(true);
12175  }
12176  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
12177  {
12178  Utilities->CallLogPop(916);
12179  return(false);
12180  }
12181  else
12182  {
12183  FormatType = TimeLoc;
12184  LocationType = AtLocation;
12185  SequenceType = IntermediateSequence;
12186  ShuttleLinkType = NotAShuttleLink;
12187  Utilities->CallLogPop(917);
12188  return(true);
12189  }
12190  }
12191  // here if second segment is a command, with a third & maybe fourth segments
12192  if((Pos != 4) && (Pos != 7) && (Pos != 8))
12193  {
12194  Utilities->CallLogPop(918);
12195  return(false);
12196  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
12197  }
12198  Second = Remainder.SubString(1, Pos - 1); // command
12199 
12200  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12201  // details
12202  Pos = Remainder.Pos(';'); // third segment delimiter
12203  if(Pos == 0)
12204  {
12205  Third = Remainder;
12206  }
12207  else
12208  {
12209  Third = Remainder.SubString(1, Pos - 1);
12210  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12211  }
12212 
12213  if((Second == "Snt") || (Second == "Snt-sh"))
12214  // third has to be 2 element idents with a space between
12215  {
12216  int SpacePos = Third.Pos(' ');
12217  if(SpacePos == 0)
12218  {
12219  Utilities->CallLogPop(919);
12220  return(false); // no space
12221  }
12222  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
12223  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
12224  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
12225  if(CheckLocationsExistInRailway)
12226  {
12227  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
12228  {
12229  Utilities->CallLogPop(920);
12230  return(false);
12231  }
12232  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
12233  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
12234  }
12235  if(Second == "Snt")
12236  {
12237  FormatType = StartNew;
12238  SequenceType = StartSequence;
12239  LocationType = NoLocation;
12240  // can't be set until know whether located or not - done in SecondPassActions
12241  ShuttleLinkType = NotAShuttleLink;
12242  }
12243  else // Snt-sh
12244  {
12245  FormatType = SNTShuttle;
12246  LocationType = AtLocation;
12247  SequenceType = StartSequence;
12248  ShuttleLinkType = ShuttleLink;
12249  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
12250  {
12251  Utilities->CallLogPop(1038);
12252  return(false);
12253  }
12254  }
12255  Utilities->CallLogPop(921);
12256  return(true);
12257  }
12258  if(Second == "Sns-sh") // third & fourth have to be headcodes
12259  {
12260  FormatType = SNSShuttle;
12261  LocationType = AtLocation;
12262  SequenceType = StartSequence;
12263  ShuttleLinkType = ShuttleLink;
12264  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
12265  {
12266  Utilities->CallLogPop(1039);
12267  return(false);
12268  }
12269  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
12270  {
12271  Utilities->CallLogPop(1040);
12272  return(false);
12273  }
12274  Utilities->CallLogPop(1041);
12275  return(true);
12276  }
12277  if(Second == "F-nshs")
12278  {
12279  FormatType = FNSNonRepeatToShuttle;
12280  LocationType = AtLocation;
12281  SequenceType = FinishSequence;
12282  ShuttleLinkType = ShuttleLink;
12283  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
12284  {
12285  Utilities->CallLogPop(1047);
12286  return(false);
12287  }
12288  Utilities->CallLogPop(1048);
12289  return(true);
12290  }
12291  if(Second == "Sns-fsh")
12292  {
12293  FormatType = SNSNonRepeatFromShuttle;
12294  LocationType = AtLocation;
12295  SequenceType = StartSequence;
12296  ShuttleLinkType = ShuttleLink;
12297  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
12298  {
12299  Utilities->CallLogPop(1098);
12300  return(false);
12301  }
12302  Utilities->CallLogPop(1099);
12303  return(true);
12304  }
12305  if(Second == "Fns-sh") // third & fourth have to be headcodes
12306  {
12307  FormatType = FSHNewService;
12308  LocationType = AtLocation;
12309  SequenceType = FinishSequence;
12310  ShuttleLinkType = ShuttleLink;
12311  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
12312  {
12313  Utilities->CallLogPop(1050);
12314  return(false);
12315  }
12316  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
12317  {
12318  Utilities->CallLogPop(1051);
12319  return(false);
12320  }
12321  Utilities->CallLogPop(1052);
12322  return(true);
12323  }
12324  // new segment for 'pas'
12325  if(Second == "pas") // third has to be a location
12326  {
12327  FormatType = PassTime;
12328  LocationType = EnRoute;
12329  SequenceType = IntermediateSequence;
12330  ShuttleLinkType = NotAShuttleLink;
12331  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
12332  {
12333  Utilities->CallLogPop(1515);
12334  return(false);
12335  }
12336  Utilities->CallLogPop(1516);
12337  return(true);
12338  }
12339  // new segment for revised 'Fer'
12340  if(Second == "Fer")
12341  // third has to be a set of IDs separated by spaces, and at least 1
12342  {
12343  FormatType = ExitRailway;
12344  LocationType = EnRoute;
12345  SequenceType = FinishSequence;
12346  ShuttleLinkType = NotAShuttleLink;
12347  if(CheckLocationsExistInRailway)
12348  {
12349  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
12350  {
12351  Utilities->CallLogPop(1519);
12352  return(false);
12353  }
12354  }
12355  Utilities->CallLogPop(1520);
12356  return(true);
12357  }
12358  if(Second == "dsc") //new at v2.15.0 - change description
12359  {
12360  if(Third.Length() > 60)
12361  {
12362  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters in '" + Third + "'");
12363  Utilities->CallLogPop(2582);
12364  return(false);
12365  }
12366  for(int x = 1; x < Third.Length() + 1; x++)
12367  {
12368 // if((Third[x] < ' ') || (Third[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12369  if((Third[x] < ' ') && (Third[x] >= 0))
12370  {
12371  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + Third + "'");
12372  Utilities->CallLogPop(2583);
12373  return(false);
12374  }
12375  }
12376  FormatType = TimeCmdDescription;
12377  LocationType = AtLocation;
12378  SequenceType = IntermediateSequence;
12379  ShuttleLinkType = NotAShuttleLink;
12380  Utilities->CallLogPop(2604);
12381  return(true);
12382  }
12383 
12384 // if((Second == "fsp") || (Second == "fsp")) then there can optionlly be a fourth: xx-yy where xx = percentage mass & yy = percentage power in the split train
12385 
12386  // all remainder must be TimeCmdHeadCode types to be valid
12387  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
12388  (Second != "Frh-sh"))
12389  {
12390  Utilities->CallLogPop(922);
12391  return(false); // all TimeCmdHeadCode types
12392  }
12393  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
12394  {
12395  Utilities->CallLogPop(923);
12396  return(false);
12397  }
12398  FormatType = TimeCmdHeadCode;
12399  LocationType = AtLocation;
12400  if(Second == "Frh-sh")
12401  {
12402  ShuttleLinkType = ShuttleLink;
12403  }
12404  else
12405  {
12406  ShuttleLinkType = NotAShuttleLink;
12407  }
12408  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
12409  {
12410  SequenceType = FinishSequence;
12411  }
12412  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
12413  {
12414  SequenceType = IntermediateSequence;
12415  }
12416  if((Second == "Sfs") || (Second == "Sns"))
12417  {
12418  SequenceType = StartSequence;
12419  }
12420  //new at v2.15.0 to allow splits to have different characteristics - Fourth specifies: AA-BB where AA & BB can be 1 or 2 digits, AA is percentage mass
12421  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //& BB is percentage power allocated to the split off train
12422  {
12423  if(!CheckFourthValidityForSplit(Fourth, GiveMessages))
12424  {
12425  Utilities->CallLogPop(2584);
12426  return(false);
12427  }
12428  }
12429  Utilities->CallLogPop(924);
12430  return(true);
12431 }
12432 
12433 // ---------------------------------------------------------------------------
12434 
12435 bool TTrainController::CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages) //new at v2.15.0
12436 {
12437  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CheckFourthValidityForSplit," + SplitDistributionString);
12438  bool ErrorFlag = false;
12439  int x, y;
12440  if((SplitDistributionString.Length() > 6 ) || (SplitDistributionString.Length() < 3))
12441  {
12442  ErrorFlag = true;
12443  }
12444  int pos = SplitDistributionString.Pos('-');
12445  if(pos == 0)
12446  {
12447  ErrorFlag = true;
12448  }
12449  else
12450  {
12451  AnsiString MassStr = SplitDistributionString.SubString(1, pos - 1);
12452  AnsiString PowerStr = SplitDistributionString.SubString(pos + 1, SplitDistributionString.Length() - pos);
12453  try //allows for one or two digit percentages
12454  {
12455  int x = MassStr.ToInt();
12456  int y = PowerStr.ToInt();
12457  if((x > 99) || (x < 1) || (y > 100) || (y < 0))
12458  {
12459  ErrorFlag = true;
12460  }
12461  }
12462  catch(const Exception &e) //non-error catch
12463  {
12464  ErrorFlag = true;
12465  }
12466  }
12467  if(ErrorFlag)
12468  {
12469  TimetableMessage(GiveMessages, "Error in split distribution " + SplitDistributionString + ", should be 'AA-BB' where AA is the percentage mass (min 1, max 99) and BB the percentage " +
12470  "power for the new split-off train");
12471  Utilities->CallLogPop(2585);
12472  return(false);
12473  }
12474  Utilities->CallLogPop(2601);
12475  return(true);
12476 }
12477 
12478 // ---------------------------------------------------------------------------
12479 
12480 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
12481 {
12482  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with 'digit-digit-colon'
12483  // and contains no control characters changed at v2.16.0 to allow extended characters in location names
12484  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
12485  if(LocStr == "")
12486  {
12487  Utilities->CallLogPop(1353);
12488  return(false); // has to have at least one character
12489  }
12490 // if((LocStr[1] >= '0') && (LocStr[1] <= '9')) //changed at v2.16.0 to allow locations to begine with digits, if 'digit-digit-colon' then must be a time
12491  if((LocStr.Length() >= 3) && (LocStr[1] >= '0') && (LocStr[1] <= '9') && (LocStr[2] >= '0') && (LocStr[2] <= '9') && (LocStr[3] == ':'))
12492  {
12493  Utilities->CallLogPop(1354);
12494  return(false); // can't begin with 'digit-digit-colon' as this regarded as a time
12495  }
12496  for(int x = 1; x < LocStr.Length() + 1; x++)
12497  {
12498  if(((LocStr[x] < ' ') && (LocStr[x] >= 0)) || (LocStr[x] == ',') || (LocStr[x] == ';')) //changed at v2.16.0 to allow extended characters in location names
12499  {
12500  Utilities->CallLogPop(1355);
12501  return(false); // contains a special character or ',' or ';'
12502  }
12503 /*
12504  if(LocStr[x] > 'z') //dropped at v2.16.0 to allow extended characters in location names
12505  {
12506  Utilities->CallLogPop(1356);
12507  return(false); // contains a character outside the standard ASCII set
12508  }
12509 */
12510  }
12511  // check exists in railway location list if CheckLocationsExistInRailway is true
12512  if(CheckLocationsExistInRailway)
12513  {
12514  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
12515  {
12516  TimetableMessage(GiveMessages, "Location name '" + LocStr +
12517  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
12518  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
12519  "that includes a continuation will not be valid.");
12520  Utilities->CallLogPop(1357);
12521  return(false);
12522  }
12523  }
12524  Utilities->CallLogPop(1358);
12525  return(true);
12526 }
12527 
12528 // ---------------------------------------------------------------------------
12529 
12530 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
12531 {
12532  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
12533  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
12534  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
12535  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
12536  HeadCode);
12537  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
12538  {
12539  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
12540  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
12541  Utilities->CallLogPop(1359);
12542  return(false);
12543  }
12544  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
12545  for(int x = 1; x < (HeadCode.Length() + 1); x++)
12546  {
12547  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
12548  {
12549  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
12550  Utilities->CallLogPop(1895);
12551  return(false);
12552  }
12553  }
12554  // secondly ensure the true Headcode only has letters or digits
12555  for(int x = 3; x >= 0; x--)
12556  {
12557  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
12558  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
12559  {
12560  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
12561  Utilities->CallLogPop(1790);
12562  return(false);
12563  }
12564  }
12565  Utilities->CallLogPop(1364);
12566  return(true);
12567 }
12568 
12569 // ---------------------------------------------------------------------------
12570 
12571 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
12572 // set of track element IDs, separated by spaces, and at least 1 present
12573 {
12574  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
12575  ExitList.clear();
12576  AnsiString CurrentID = "";
12577 
12578  if(IDSet.Length() == 0)
12579  {
12580  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
12581  Utilities->CallLogPop(1521);
12582  return(false);
12583  }
12584  for(int x = 1; x <= IDSet.Length(); x++)
12585  {
12586  char C = IDSet[x];
12587  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
12588  {
12589  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
12590  Utilities->CallLogPop(1522);
12591  return(false);
12592  }
12593 /* don't use, error checks in GetTrackVectorPositionFromString instead
12594  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
12595  {
12596  if((x==1) || (x == IDSet.Length()))
12597  {
12598  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12599  Utilities->CallLogPop(2479);
12600  return(false);
12601  }
12602  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
12603  {
12604  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12605  Utilities->CallLogPop(2480);
12606  return(false);
12607  }
12608  }
12609 */
12610  }
12611  int Pos = IDSet.Pos(' '); // look for the first space
12612 
12613  while(true)
12614  {
12615  if(Pos == 0)
12616  {
12617  CurrentID = IDSet;
12618  IDSet = "";
12619  }
12620  else
12621  {
12622  CurrentID = IDSet.SubString(1, Pos - 1);
12623  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
12624  }
12625  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
12626  if(VecPos == -1)
12627  {
12628  Utilities->CallLogPop(1523);
12629  return(false); // messages given in GetTrackVectorPositionFromString
12630  }
12631  else
12632  {
12633  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
12634  {
12635  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12636  Utilities->CallLogPop(1524);
12637  return(false);
12638  }
12639  else
12640  {
12641  // first check for duplicates
12642  if(!ExitList.empty())
12643  {
12644  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12645  {
12646  if(*ELIT == VecPos)
12647  {
12648  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12649  Utilities->CallLogPop(1532);
12650  return(false);
12651  }
12652  }
12653  }
12654  // of OK add it to the list
12655  ExitList.push_back(VecPos);
12656  }
12657  }
12658  if(IDSet == "")
12659  {
12660  Utilities->CallLogPop(1525);
12661  return(true);
12662  }
12663  else
12664  {
12665  Pos = IDSet.Pos(' '); // look for the next space
12666  }
12667  } // while(true)
12668 }
12669 
12670 // ---------------------------------------------------------------------------
12671 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12672  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12673 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12674 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12675 // of each item
12676 {
12677  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12678  int Pos = 0;
12679  AnsiString Remainder = "";
12680  int SemiColonCount = 0;
12681 
12682  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12683  {
12684  if(TrainInfoStr[x] == ';')
12685  {
12686  SemiColonCount++;
12687  }
12688  }
12689  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12690  {
12691  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12692  "'. Should be headcode + optional description for a continuing service;" +
12693  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12694  Utilities->CallLogPop(880);
12695  return(false);
12696  }
12697  if(SemiColonCount == 0)
12698  {
12699  HeadCode = TrainInfoStr;
12700  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12701  {
12702  Utilities->CallLogPop(881);
12703  return(false);
12704  }
12705  Utilities->CallLogPop(882);
12706  return(true);
12707  }
12708  if(SemiColonCount == 1) // headcode & description only
12709  {
12710  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12711  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12712  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12713  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12714  {
12715  Utilities->CallLogPop(883);
12716  return(false);
12717  }
12718  if(Description == "")
12719  {
12720  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12721  Utilities->CallLogPop(884);
12722  return(false);
12723  }
12724  if(Description.Length() > 60)
12725  {
12726  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12727  Utilities->CallLogPop(1157);
12728  return(false);
12729  }
12730  for(int x = 1; x < Description.Length() + 1; x++)
12731  {
12732 // if((Description[x] < ' ') || (Description[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12733  if((Description[x] < ' ') && (Description[x] >= 0))
12734  {
12735  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12736  Utilities->CallLogPop(885);
12737  return(false);
12738  }
12739  }
12740  Utilities->CallLogPop(886);
12741  return(true);
12742  }
12743  // if here must have 6 or 7 semicolons
12744  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12745  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12746  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12747  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12748  {
12749  Utilities->CallLogPop(887);
12750  return(false);
12751  }
12752  Pos = Remainder.Pos(';'); // 2nd delimiter
12753  Description = Remainder.SubString(1, Pos - 1);
12754  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12755  if(Description == "")
12756  {
12757  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12758  Utilities->CallLogPop(888);
12759  return(false);
12760  }
12761  if(Description.Length() > 60)
12762  {
12763  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12764  Utilities->CallLogPop(1158);
12765  return(false);
12766  }
12767  for(int x = 1; x < Description.Length() + 1; x++)
12768  {
12769 // if((Description[x] < ' ') || (Description[x] > 126)) changed at v2.16.0 to allow extended characters in location names
12770  if((Description[x] < ' ') && (Description[x] >= 0))
12771  {
12772  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12773  Utilities->CallLogPop(889);
12774  return(false);
12775  }
12776  }
12777  Pos = Remainder.Pos(';'); // 3rd delimiter
12778  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12779 
12780  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12781  if(StartSpeedStr == "")
12782  {
12783  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12784  Utilities->CallLogPop(890);
12785  return(false);
12786  }
12787  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12788  {
12789  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12790  {
12791  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12792  Utilities->CallLogPop(891);
12793  return(false);
12794  }
12795  }
12796  StartSpeed = StartSpeedStr.ToInt();
12797  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12798  {
12799  StartSpeed = TTrain::MaximumSpeedLimit;
12800  if(!SSHigh) // added at v2.4.0
12801  {
12802  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12803  SSHigh = true;
12804  }
12805  }
12806  Pos = Remainder.Pos(';'); // 4th delimiter
12807  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12808 
12809  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12810  if(MaxRunningSpeedStr == "")
12811  {
12812  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12813  Utilities->CallLogPop(892);
12814  return(false);
12815  }
12816  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12817  {
12818  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12819  {
12820  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12821  Utilities->CallLogPop(893);
12822  return(false);
12823  }
12824  }
12825  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12826  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12827  {
12828  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12829  if(!MRSHigh) // added at v2.4.0
12830  {
12831  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12832  MRSHigh = true;
12833  }
12834  }
12835  if(MaxRunningSpeed < 10)
12836  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12837  {
12838  MaxRunningSpeed = 10;
12839  if(!MRSLow) // added at v2.4.0
12840  {
12841  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12842  MRSLow = true;
12843  }
12844  }
12845  Pos = Remainder.Pos(';'); // 5th delimiter
12846  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12847 
12848  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12849  if(MassStr == "")
12850  {
12851  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12852  Utilities->CallLogPop(895);
12853  return(false);
12854  }
12855  for(int x = 1; x < MassStr.Length() + 1; x++)
12856  {
12857  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12858  {
12859  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12860  Utilities->CallLogPop(896);
12861  return(false);
12862  }
12863  }
12864  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12865  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12866  {
12867  Mass = TTrain::MaximumMassLimit;
12868  if(!MassHigh) // added at v2.4.0
12869  {
12870  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12871  MassHigh = true;
12872  }
12873  }
12874  if(Mass == 0)
12875  {
12876  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12877  Utilities->CallLogPop(897);
12878  return(false);
12879  }
12880  Pos = Remainder.Pos(';'); // 6th delimiter
12881  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12882 
12883  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12884  if(MaxBrakeForceStr == "")
12885  {
12886  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12887  Utilities->CallLogPop(898);
12888  return(false);
12889  }
12890  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12891  {
12892  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12893  {
12894  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12895  Utilities->CallLogPop(899);
12896  return(false);
12897  }
12898  }
12899  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12900 
12901  // convert to kg force
12902  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12903  {
12904  MaxBrakeForce = Mass;
12905  if(!BFHigh) // added at v2.4.0
12906  {
12907  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12908  BFHigh = true;
12909  }
12910  }
12911  if((MaxBrakeForce / Mass) < 0.01)
12912  {
12913  MaxBrakeForce = Mass * 0.01;
12914  if(!BFLow) // added at v2.4.0
12915  {
12916  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12917  BFLow = true;
12918  }
12919  }
12920  // convert to m/s/s
12921  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12922  // now may have just a power entry or power and signaller max. speed
12923  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12924 
12925  if(SemiColonCount == 6)
12926  {
12927  GrossPowerStr = Remainder;
12928  SignallerSpeedStr = "30"; // default value
12929  }
12930  else // must be 7
12931  {
12932  Pos = Remainder.Pos(';'); // 7th delimiter
12933  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12934  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12935  }
12936  // deal with GrossPower
12937  if(GrossPowerStr == "")
12938  {
12939  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12940  Utilities->CallLogPop(901);
12941  return(false);
12942  }
12943  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12944  {
12945  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12946  {
12947  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12948  Utilities->CallLogPop(902);
12949  return(false);
12950  }
12951  }
12952 
12953  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12954 
12955  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12956  {
12957  GrossPower = TTrain::MaximumPowerLimit;
12958  if(!PwrHigh)
12959  {
12960  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12961  PwrHigh = true;
12962  }
12963  }
12964  else if(GrossPower == 0) // changed at v2.4.0
12965  {
12966  GrossPower = 0.1;
12967  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12968  }
12969  else if((GrossPower > 0) && (GrossPower < 10000))
12970  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12971  {
12972  GrossPower = 10000;
12973  }
12974  PowerAtRail = GrossPower * 0.8;
12975  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12976 
12977  // deal with SignallerSpeed
12978  if(SignallerSpeedStr == "")
12979  {
12980  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12981  Utilities->CallLogPop(1771);
12982  return(false);
12983  }
12984  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12985  {
12986  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12987  {
12988  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12989  Utilities->CallLogPop(1769);
12990  return(false);
12991  }
12992  }
12993  SignallerSpeed = SignallerSpeedStr.ToInt();
12994  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12995  {
12996  SignallerSpeed = TTrain::MaximumSpeedLimit;
12997  if(!SigSHigh)
12998  {
12999  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
13000  SigSHigh = true;
13001  }
13002  }
13003  if(SignallerSpeed < 10)
13004  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
13005  {
13006  SignallerSpeed = 10;
13007  if(!SigSLow)
13008  {
13009  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
13010  SigSLow = true;
13011  }
13012  // Utilities->CallLogPop(1770);
13013  // return false;
13014  }
13015  Utilities->CallLogPop(904);
13016  return(true);
13017 }
13018 
13019 // ---------------------------------------------------------------------------
13020 
13021 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
13022  bool GiveMessages)
13023 {
13024  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
13025  // function checks validity of each item and returns false for error
13026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
13027  if(OneEntry.Length() < 7)
13028  {
13029  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
13030  Utilities->CallLogPop(865);
13031  return(false);
13032  }
13033  int SemiColonCount = 0;
13034 
13035  for(int x = 1; x < OneEntry.Length() + 1; x++)
13036  {
13037  if(OneEntry[x] == ';')
13038  {
13039  SemiColonCount++;
13040  }
13041  }
13042  if(SemiColonCount != 3)
13043  {
13044  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
13045  Utilities->CallLogPop(866);
13046  return(false);
13047  }
13048  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
13049  {
13050  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
13051  Utilities->CallLogPop(867);
13052  return(false);
13053  }
13054  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
13055  // strip off R;
13056 
13057  int Pos = 0;
13058 
13059  Pos = Remainder.Pos(';');
13060  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
13061 
13062  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
13063  if(MinutesStr == "")
13064  {
13065  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
13066  Utilities->CallLogPop(868);
13067  return(false);
13068  }
13069  if(MinutesStr.Length() > 3)
13070  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
13071  {
13072  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
13073  Utilities->CallLogPop(2119);
13074  return(false);
13075  }
13076  for(int x = 1; x < MinutesStr.Length() + 1; x++)
13077  {
13078  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
13079  {
13080  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
13081  Utilities->CallLogPop(869);
13082  return(false);
13083  }
13084  }
13085  RearStartOrRepeatMins = MinutesStr.ToInt();
13086  if(RearStartOrRepeatMins == 0)
13087  {
13088  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
13089  Utilities->CallLogPop(870);
13090  return(false);
13091  }
13092  Pos = Remainder.Pos(';');
13093  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
13094 
13095  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
13096  if(DigitsStr == "")
13097  {
13098  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
13099  Utilities->CallLogPop(871);
13100  return(false);
13101  }
13102  for(int x = 1; x < DigitsStr.Length() + 1; x++)
13103  {
13104  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
13105  {
13106  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
13107  Utilities->CallLogPop(872);
13108  return(false);
13109  }
13110  }
13111  if(DigitsStr.Length() > 2)
13112  {
13113  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
13114  Utilities->CallLogPop(873);
13115  return(false);
13116  }
13117  FrontStartOrRepeatDigits = DigitsStr.ToInt();
13118 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
13119  route rather than the service
13120  if(FrontStartOrRepeatDigits == 0)
13121  {
13122  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
13123  Utilities->CallLogPop(874);
13124  return false;
13125  }
13126 */
13127  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
13128  // new for v0.6b for unrestricted headcodes
13129  {
13130  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
13131  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
13132  Utilities->CallLogPop(1889);
13133  return(false);
13134  }
13135  AnsiString NumberStr = Remainder;
13136 
13137  if(NumberStr == "")
13138  {
13139  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
13140  Utilities->CallLogPop(875);
13141  return(false);
13142  }
13143  if(NumberStr.Length() > 4)
13144  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
13145  {
13146  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
13147  Utilities->CallLogPop(2118);
13148  return(false);
13149  }
13150  for(int x = 1; x < NumberStr.Length() + 1; x++)
13151  {
13152  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
13153  // catches negative numbers
13154  {
13155  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
13156  Utilities->CallLogPop(876);
13157  return(false);
13158  }
13159  }
13160  NumberOfRepeats = NumberStr.ToInt();
13161  if(NumberOfRepeats == 0)
13162  {
13163  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
13164  Utilities->CallLogPop(877);
13165  return(false);
13166  }
13167  Utilities->CallLogPop(878);
13168  return(true);
13169 }
13170 
13171 // ---------------------------------------------------------------------------
13172 
13173 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
13174 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
13175  vector rather than the timetable
13176  Note also that for unlocated Snt entries the LocationType hasn't yet been set
13177 
13178  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
13179 
13180  For info:-
13181  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
13182  {
13183  public:
13184  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
13186  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
13187  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
13188  int NumberOfRepeats; ///< the number of repeating services
13189  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
13191  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
13193  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
13194  TTimetableFormatType FormatType; ///< defines the timetable action type
13195  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
13196  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
13197  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
13198  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
13200  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
13202 
13203  // inline function
13204 
13206  TActionVectorEntry() {
13207  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
13208  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
13209  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
13210  Warning = false; SignallerControl = false;
13211  }
13212  };
13213 
13214  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
13215 
13216  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
13217  {
13218  public:
13219  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
13222  double MaxBrakeRate; ///< in metres/sec/sec
13223  double MaxRunningSpeed; ///< in km/h
13224  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
13225  int Mass; ///< in kg
13226  int NumberOfTrains; ///< number of repeats + 1
13227  int SignallerSpeed; ///< in km/h for use when under signaller control
13228  int StartSpeed; ///< in km/h
13229  TActionVector ActionVector; ///< all the actions for the train
13230  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
13231 
13232  //inline function
13233 
13235  TTrainDataEntry()
13236  {
13237  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
13238  }
13239  };
13240 
13241  Allowable successors:-
13242  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
13243  Snt located -> No starts, no finishes except Frh, Fjo (as of v2.0.0), Fns, and F-nshs, no repeat, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
13244  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
13245  Sfs -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc, TimeLoc arr, rsp, fsp; any other cmd or TimeLoc (dep) OK [
13246  must have departure & arrival before another split]
13247  Sns -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
13248  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
13249  set location, else fails)
13250  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
13251  set location, else fails)
13252  Fns -> R only [must be preceded by a TimeLoc arrival at the finish location, not necessarily immediately]
13253  F-nshs -> Nothing (no repeats permitted)
13254  Fjo -> R only
13255  Frh -> R only
13256  Fer -> R only
13257  Frh-sh -> R only
13258  Fns-sh -> R only
13259  jbo -> No starts, repeats, pas, Fer or TimeTimeLoc; TimeLoc (dep), others OK [must be preceded by an event whose location is set]
13260  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13261  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13262  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13263  dsc -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13264  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
13265  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13266  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13267  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13268  Repeat -> Nothing
13269 
13270  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
13271  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
13272  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
13273  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
13274  Check all times increase or stay same through ActionVector
13275  Cycle through all entries in vector setting arr & dep times based on above list
13276  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
13277  Check locations match the arr & dep TimeLoc entries
13278  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
13279  Make above valid succession checks
13280  Check all splits have matching Sfs headcodes (both ways), add locations to Sfs's & check times same
13281  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to Sns's & check times same
13282  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13283  Check each Fns has matching Sns headcodes (both ways), add locations to Fns's & check times same
13284  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
13285  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13286  Set train info for Sfs & Sns entries
13287  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
13288  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
13289  element at each end, or length of 3 & 1 extra element at either end
13290  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
13291  Check all Cmds have EventTime set & Arr & Dep times = -1
13292  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
13293 
13294  Give messages in function if errors detected and clear the vector. Return false for failure.
13295 */
13296 
13297 /* Earlier checks:-
13298  Checks carried out with error messages in this function:-
13299  At least one comma in the line (it's based on a csv file);
13300  No entries following train information;
13301  At least one comma in remainder after train information (i.e at least a start and a finish entry);
13302  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
13303  First entry not a start entry;
13304  Train information incomplete before a start entry;
13305  Entry follows a finish entry but doesn't begin with 'R';
13306  SplitEntry returns false in a finish entry - message repeats the entry for information;
13307  Last action entry isn't a finish entry.
13308 
13309  Function returns false with no message if:-
13310  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
13311  time is found at all then an error message is given in the calling function);
13312  SplitTrainInfo returns false (message given in called function);
13313  SplitRepeat returns false (message given in called function).
13314 
13315 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
13316 
13317 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
13318  Code ShuttleLink- EntryPtr ShuttleLink
13319  HeadCode EntryPtr
13320 
13321 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
13322 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
13323 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
13324 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (fdr-shld be rtn)N (shld be fdr) Luckily NonRep link not needed
13325 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
13326 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
13327 
13328 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
13329 
13330 */
13331 {
13332  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
13333  if(TrainDataVector.empty())
13334  {
13335  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
13336  TrainDataVector.clear();
13337  Utilities->CallLogPop(1832);
13338  return(false);
13339  }
13340 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
13341  1) must have at least one actionvector entry
13342  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
13343  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
13344  4) first entry must be a start;
13345  4a) if first entry is Snt and second is a finish then it can't be Fns-sh or Frh-sh
13346  4b) if first entry is Sns or Sfs and second is a finish then it must be either Frh or Fjo
13347  4c) if first entry is Snt-sh, Sns-sh or Sns-fsh second can't be a finish
13348  5) a start must be the first entry;
13349  6) a repeat entry must be the last;
13350  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
13351  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
13352  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
13353 */
13354 
13355  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
13356  TwoLocationFlag = false; //added at v2.9.1
13357  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
13358  {
13359  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13360  if(TrainDataVector.at(x).ActionVector.empty())
13361  {
13362  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
13363  TrainDataVector.clear();
13364  Utilities->CallLogPop(1833);
13365  return(false);
13366  }
13367  }
13368  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
13369  {
13370  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13371  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13372  if(!(AVEntry0.SignallerControl))
13373  {
13374  if(TrainDataVector.at(x).ActionVector.size() == 1)
13375  {
13376  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
13377  TrainDataVector.clear();
13378  Utilities->CallLogPop(1822);
13379  return(false);
13380  }
13381  }
13382  }
13383  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
13384  {
13385  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13386  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13387  if(AVEntry0.SignallerControl)
13388  {
13389  if(TrainDataVector.at(x).ActionVector.size() > 2)
13390  {
13391  SecondPassMessage(GiveMessages,
13392  "Error in timetable - a signaller controlled service can have no more than one item (a repeat) after the start event, see: " +
13393  TDEntry.HeadCode);
13394  TrainDataVector.clear();
13395  Utilities->CallLogPop(1837);
13396  return(false);
13397  }
13398  if(TrainDataVector.at(x).ActionVector.size() > 1)
13399  {
13400  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13401  if(AVEntry1.FormatType != Repeat)
13402  {
13403  SecondPassMessage(GiveMessages,
13404  "Error in timetable - a signaller controlled service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
13405  TrainDataVector.clear();
13406  Utilities->CallLogPop(1838);
13407  return(false);
13408  }
13409  }
13410  }
13411  }
13412  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
13413  {
13414  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13415  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13416  if(AVEntry0.SequenceType != StartSequence)
13417  {
13418  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
13419  TrainDataVector.clear();
13420  Utilities->CallLogPop(1824);
13421  return(false);
13422  }
13423  if((AVEntry0.Command == "Snt") && !AVEntry0.SignallerControl) // (4a) sig control condition added so there is a second AVEntry
13424  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
13425  // and others for a located Snt, but those checks done later
13426  {
13427  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1); // must be a second entry if first not signallercontrol
13428  if((AVEntry1.SequenceType == FinishSequence) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
13429  {
13430  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
13431  TDEntry.HeadCode); //these are the only AtLoc finishes not allowed
13432  TrainDataVector.clear();
13433  Utilities->CallLogPop(2046);
13434  return(false);
13435  }
13436  }
13437  if((AVEntry0.Command == "Sns") || (AVEntry0.Command == "Sfs")) // (4b)
13438  // 4b added at v2.15.0
13439  {
13440  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13441  if((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo"))
13442  {
13443  SecondPassMessage(GiveMessages, "Error in timetable - only 'Frh' or 'Fjo' finish events are permitted immediately after an 'Sns' or 'Sfs' event for: " +
13444  TDEntry.HeadCode);
13445  TrainDataVector.clear();
13446  Utilities->CallLogPop(2580);
13447  return(false);
13448  }
13449  }
13450  if((AVEntry0.Command == "Snt-sh") || (AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh")) // (4c)
13451  // 4c added at v2.15.0
13452  {
13453  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13454  if(AVEntry1.SequenceType == FinishSequence)
13455  {
13456  SecondPassMessage(GiveMessages, "Error in timetable - a finish event can't immediately follow an 'Snt-sh', 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13457  TrainDataVector.clear();
13458  Utilities->CallLogPop(2616);
13459  return(false);
13460  }
13461  }
13462  }
13463  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
13464  {
13465  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13466  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13467  {
13468  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13469  if((AVEntry.SequenceType == StartSequence) && (y != 0))
13470  {
13471  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
13472  TrainDataVector.clear();
13473  Utilities->CallLogPop(1825);
13474  return(false);
13475  }
13476  }
13477  }
13478  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
13479  {
13480  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13481  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13482  {
13483  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13484  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
13485  {
13486  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
13487  TrainDataVector.clear();
13488  Utilities->CallLogPop(1826);
13489  return(false);
13490  }
13491  }
13492  }
13493  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
13494  {
13495  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13496  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13497  {
13498  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13499  if((y == 0) && AVEntry.SignallerControl)
13500  {
13501  break;
13502  }
13503  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
13504  {
13505  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != FinishSequence))
13506  {
13507  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
13508  TrainDataVector.clear();
13509  Utilities->CallLogPop(1827);
13510  return(false);
13511  }
13512  if(AVEntry.FormatType == Repeat)
13513  {
13514  const TActionVectorEntry &LastButOneAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
13515  if(LastButOneAVEntry.SequenceType != FinishSequence)
13516  {
13517  SecondPassMessage(GiveMessages, "Error in timetable - the event immediately before the repeat must be a finish for: " + TDEntry.HeadCode);
13518  TrainDataVector.clear();
13519  Utilities->CallLogPop(1828);
13520  return(false);
13521  }
13522  }
13523  }
13524  }
13525  }
13526  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
13527  {
13528  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13529  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13530  {
13531  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13532  if(AVEntry.SequenceType == FinishSequence)
13533  {
13534  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
13535  {
13536  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
13537  TrainDataVector.clear();
13538  Utilities->CallLogPop(1829);
13539  return(false);
13540  }
13541  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
13542  {
13543  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13544  {
13545  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " + TDEntry.HeadCode);
13546  TrainDataVector.clear();
13547  Utilities->CallLogPop(1830);
13548  return(false);
13549  }
13550  }
13551  }
13552  }
13553  }
13554 
13555  // end of new preliminary checks
13556 
13557  // check start event successor validity
13558  // For Snt & Snt-sh set location if stopped, don't set any times yet
13559  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13560  {
13561  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13562  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13563  // use reference so can change internals where necessary
13564  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
13565  {
13566  AnsiString LocationName = "";
13567  if(IsSNTEntryLocated(0, TDEntry, LocationName))
13568  // it is at a location
13569  {
13570  AVEntry0.LocationName = LocationName; //located Snt location name set
13571  AVEntry0.LocationType = AtLocation;
13572  // check successor validity for located Snt that isn't a SignallerControl entry
13573  if(!AVEntry0.SignallerControl)
13574  {
13575  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13576  // at least 2 entries present checked in integrity check so (1) valid
13577  if(!AtLocSuccessor(AVEntry1))
13578  {
13579  // Frh following Snt-sh will return false in location check, so no need to check here
13580  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
13581  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13582  TrainDataVector.clear();
13583  Utilities->CallLogPop(523);
13584  return(false);
13585  }
13586  }
13587  }
13588  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
13589  {
13590  if(AVEntry0.Command == "Snt-sh")
13591  {
13592  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
13593  TrainDataVector.clear();
13594  Utilities->CallLogPop(1042);
13595  return(false);
13596  }
13597  AVEntry0.LocationType = EnRoute;
13598  if(!AVEntry0.SignallerControl)
13599  {
13600  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13601  // at least 2 entries checked in integrity check so (1) valid
13602  if(!MovingSuccessor(AVEntry1))
13603  {
13604  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
13605  TDEntry.HeadCode);
13606  TrainDataVector.clear();
13607  Utilities->CallLogPop(790);
13608  return(false);
13609  }
13610  }
13611  }
13612  }
13613  // check other start successors, all AtLoc
13614  else if(AVEntry0.SequenceType == StartSequence)
13615  {
13616  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13617  // at least 2 entries present checked in integrity check so (1) valid
13618  if(!AtLocSuccessor(AVEntry1))
13619  {
13620  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh', 'Snt-fsh' or 'Sns-fsh' followed by an illegal event for: " +
13621  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13622  TrainDataVector.clear();
13623  Utilities->CallLogPop(793);
13624  return(false);
13625  }
13626  }
13627  }
13628 
13629 
13630  // set Sns-sh & Sns-fsh locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
13631  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13632  {
13633  bool FoundFlag = false;
13634  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13635  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13636  // use reference so can change internals
13637  if((AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh"))
13638  {
13639  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13640  {
13641  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
13642  if(AVEntry2.FormatType == TimeLoc)
13643  {
13644  FoundFlag = true;
13645  AVEntry0.LocationName = AVEntry2.LocationName; //Sns-sh & Sns-fsh location names set
13646  break;
13647  }
13648  }
13649  if(!FoundFlag)
13650  {
13651  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13652  TrainDataVector.clear();
13653  Utilities->CallLogPop(851);
13654  return(false);
13655  }
13656  }
13657  }
13658 
13659 //carry out preliminary check on service ref linkages without setting any data - added at v2.15.0 as can be location errors if linked trains not present
13660 //first check for duplicates then linkages (also checked later but leave that in)
13661  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13662  {
13663  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13664  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13665  {
13666  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13667  if(AVEntry.OtherHeadCode != "")
13668  {
13669  if(!CheckForDuplicateCrossReferences(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13670  {
13671  Utilities->CallLogPop(2610);
13672  return(false); // error message given in called function
13673  }
13674  }
13675  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13676  {
13677  if(!CheckForDuplicateCrossReferences(3, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13678  {
13679  Utilities->CallLogPop(2611);
13680  return(false); // error message given in called function
13681  }
13682  }
13683  }
13684  }
13685 //cross reference check
13686  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13687  {
13688  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13689  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13690  {
13691  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13692  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13693  {
13694  if(AVEntry.OtherHeadCode != "")
13695  {
13696  if(!CheckCrossReferencesAndSetData(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, false, GiveMessages))
13697  // false = non-shuttle
13698  {
13699  Utilities->CallLogPop(2612);
13700  return(false); // error message given in called function
13701  }
13702  }
13703  }
13704  }
13705  }
13706 
13707 // now repeat the check just for the shuttles
13708  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13709  {
13710  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13711  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13712  {
13713  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13714  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13715  {
13716  if(AVEntry.OtherHeadCode != "")
13717  {
13718  if(!CheckCrossReferencesAndSetData(3, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, false, GiveMessages))
13719  // true = shuttle
13720  {
13721  Utilities->CallLogPop(2613);
13722  return(false); // error message given in called function
13723  }
13724  }
13725  }
13726  }
13727  }
13728 
13729 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13730  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13731  {
13732  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13733  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13734  {
13735  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13736  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13737  {
13738  if(!CheckNonRepeatingShuttleLinksAndSetData(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, false, GiveMessages))
13739  {
13740  Utilities->CallLogPop(2614);
13741  return(false); // error message given in called function
13742  }
13743  }
13744  }
13745  }
13746 
13747 
13748 //at v2.15.0 we want to set Sns, Sfs location names, but to set Sns & Sfs first need the linked Fns & fsp/rsp to have locations set as they aren't yet,
13749 //and before v2.15.0 they were set from the corresponding Sns & Sfs locations, which in turn were set from later TimeLoc departures. At v2.15.0 it is required to have
13750 //these commands followed by Frh & Fjo, so this is why we need the linked Fns & fsp/rsp to have locations set first. Now all Fns will have a TimeLoc before, so
13751 //that can provide its location, but fsp/rsp? Must they have a TimeLoc before? No, and can't rely on starting Sfs having the location set yet.
13752 //So, new restriction, insist on an rsp/fsp having a TimeLoc before it or a located Snt, and use one of those to set the location for the rsp/fsp and hence the linked Sfs.
13753 
13754 //NB can't allow an Sfs to be followed by another split or won't find a name, test with many existing tts then add an error to find it
13755 //Fns must be preceded by an arrival
13756 
13757 //set name for Fns from earlier location name or fail if can't find
13758  bool LocFoundFlag, FnsFoundFlag;
13759  TActionVectorEntry *AVEntryFns;
13760  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13761  {
13762  LocFoundFlag = false;
13763  FnsFoundFlag = false;
13764  for(int y = TrainDataVector.at(x).ActionVector.size() - 1; y >= 0; y--)
13765  {
13766  if(TrainDataVector.at(x).ActionVector.at(y).Command == "Fns")
13767  {
13768  AVEntryFns = &TrainDataVector.at(x).ActionVector.at(y);
13769  FnsFoundFlag = true;
13770  continue;
13771  }
13772  if(!FnsFoundFlag)
13773  {
13774  continue;
13775  }
13776  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "")
13777  {
13778  LocFoundFlag = true;
13779  AVEntryFns->LocationName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13780 // double EVT = double(AVEntryFns->EventTime); //test
13781  break; //name found
13782  }
13783  if(TrainDataVector.at(x).ActionVector.at(y).LocationType == AtLocation) //not named yet
13784  {
13785  continue;
13786  }
13787  else
13788  {
13789  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13790  TrainDataVector.clear();
13791  Utilities->CallLogPop(2596);
13792  return(false);
13793  }
13794  }
13795  if(FnsFoundFlag && !LocFoundFlag)
13796  {
13797  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13798  TrainDataVector.clear();
13799  Utilities->CallLogPop(2597);
13800  return(false);
13801  }
13802  }
13803 
13804 //now set all names for Sns entries from the above, new at v2.15.0
13805  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13806  {
13807  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13808  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13809  // use reference so can change internals
13810  if(AVEntry0.Command == "Sns")
13811  {
13812  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13813  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13814  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13815  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13816  //at end of this function
13817  //need to be the same: forward & reverse service refs, event times, commands correspond
13818 
13819  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13820  if(TDEntry.ActionVector.size() < 2)
13821  {
13822  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sns' event for: " + TDEntry.HeadCode);
13823  TrainDataVector.clear();
13824  Utilities->CallLogPop(2598);
13825  return(false);
13826  }
13827  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13828  if(!AtLocSuccessor(AVEntry1))
13829  {
13830  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode +
13831  ". The event isn't valid for a stationary train.");
13832  TrainDataVector.clear();
13833  Utilities->CallLogPop(2599);
13834  return(false);
13835  }
13836  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13837  (AVEntry1.FormatType == Repeat))
13838  {
13839  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode);
13840  TrainDataVector.clear();
13841  Utilities->CallLogPop(2600);
13842  return(false);
13843  }
13844 
13845  //now set the location and location type
13846  TDateTime SnsEventTime = AVEntry0.EventTime;
13847 // double EVT = double(SnsEventTime); //test
13848  AnsiString SnsServiceRef = TDEntry.ServiceReference;
13849  bool BreakFlag = false;
13850  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13851  {
13852  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13853  {
13854  if((TrainDataVector.at(y).ActionVector.at(z).Command == "Fns") && (SnsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) &&
13855  (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13856  { //forward linkage found
13857  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SnsServiceRef) //OtherHeadCode values are service refs, see above
13858  { //reverse linkage found
13859  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13860  AVEntry0.LocationType = AtLocation;
13861  BreakFlag = true;
13862  break;
13863  }
13864  }
13865  }
13866  if(BreakFlag)
13867  {
13868  break;
13869  }
13870  }
13871  //test for any unnamed AtLoc entries at end of name setting
13872  }
13873  }
13874 
13875 //trap errors where rsp/fsp follows an Sfs without a TimeLoc arrival before (or unlikely to be able to set fsp/rsp/Sfs location because Sfs locs set from linked fsp/rsp events)
13876  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13877  {
13878  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13879  if(AVEntry0.Command == "Sfs")
13880  {
13881  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13882  {
13883  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "") //must be a timeloc as only they have loc set and are AtLoc (non-AtLoc trapped above)
13884  {
13885  break;
13886  }
13887  else if((TrainDataVector.at(x).ActionVector.at(y).Command == "fsp") || (TrainDataVector.at(x).ActionVector.at(y).Command == "rsp"))
13888  {
13889  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event must be followed by a departure and arrival before another split, see " + TrainDataVector.at(x).ServiceReference);
13890  TrainDataVector.clear();
13891  Utilities->CallLogPop(2586);
13892  return(false);
13893  }
13894  }
13895  }
13896  }
13897 
13898 //now name fsp/rsp actions
13899  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13900  {
13901  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13902  {
13903  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13904  if(AVEntry.LocationName != "")
13905  {
13906  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13907  {
13908  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13909  // use reference so can change internals where necessary
13910  if((AVEntry2.Command == "fsp") || (AVEntry2.Command == "rsp"))
13911  {
13912  AVEntry2.LocationName = AVEntry.LocationName;
13913  } //test for any unnamed AtLoc entries at end of name setting
13914  else if(AVEntry2.LocationType != AtLocation)
13915  {
13916  break;
13917  }
13918  }
13919  }
13920  }
13921  }
13922 
13923 //check that all named or give error message
13924  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13925  {
13926  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13927  {
13928  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13929  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13930  {
13931  if(AVEntry.LocationName == "")
13932  {
13933  SecondPassMessage(GiveMessages, "Error in timetable - an 'fsp' or 'rsp' event must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13934  TrainDataVector.clear();
13935  Utilities->CallLogPop(2617);
13936  return(false);
13937  }
13938  }
13939  }
13940  }
13941 
13942 //now set all Sfs entries from the above
13943  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13944  {
13945  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13946  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13947  // use reference so can change internals
13948  if(AVEntry0.Command == "Sfs")
13949  {
13950  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13951  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13952  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13953  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13954  //at end of this function
13955  //need to be the same: forward & reverse service refs, event times, commands correspond
13956 
13957  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13958  if(TDEntry.ActionVector.size() < 2)
13959  {
13960  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sfs' event for: " + TDEntry.HeadCode);
13961  TrainDataVector.clear();
13962  Utilities->CallLogPop(2587);
13963  return(false);
13964  }
13965  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13966  if(!AtLocSuccessor(AVEntry1))
13967  {
13968  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode +
13969  ". The event isn't valid for a stationary train.");
13970  TrainDataVector.clear();
13971  Utilities->CallLogPop(2588);
13972  return(false);
13973  }
13974  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13975  (AVEntry1.FormatType == Repeat))
13976  {
13977  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode);
13978  TrainDataVector.clear();
13979  Utilities->CallLogPop(2589);
13980  return(false);
13981  }
13982 
13983  //now set the location and location type
13984  TDateTime SfsEventTime = AVEntry0.EventTime;
13985  AnsiString SfsServiceRef = TDEntry.ServiceReference;
13986  bool BreakFlag = false;
13987  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13988  {
13989  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13990  {
13991  if(((TrainDataVector.at(y).ActionVector.at(z).Command == "fsp") || (TrainDataVector.at(y).ActionVector.at(z).Command == "rsp")) &&
13992  (SfsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) && (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13993  { //forward linkage found
13994  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SfsServiceRef) //OtherHeadCode values are service refs, see above
13995  { //reverse linkage found
13996  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13997  AVEntry0.LocationType = AtLocation;
13998  BreakFlag = true;
13999  break;
14000  }
14001  }
14002  }
14003  if(BreakFlag)
14004  {
14005  break;
14006  }
14007  } //test for any unnamed AtLoc entries at end of name setting
14008  }
14009  }
14010 
14011  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
14012  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14013  {
14014  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14015  {
14016  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14017  if(AVEntry.LocationName != "")
14018  {
14019  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
14020  {
14021  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
14022  // use reference so can change internals where necessary
14023  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
14024  {
14025  AVEntry2.LocationName = AVEntry.LocationName;
14026  } //test for any unnamed AtLoc entries at end of name setting
14027  else
14028  {
14029  break;
14030  }
14031  }
14032  }
14033  }
14034  }
14035  // all location names should now be set
14036 
14037 //now test for any unnamed AtLoc entries where Command != "" and give message if find any - shouldn't find any if above checks comprehensive
14038  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14039  {
14040  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14041  {
14042  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14043  if((AVEntry.LocationType == AtLocation) && (AVEntry.LocationName == "") && (AVEntry.Command != ""))
14044  {
14045  if((AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc"))
14046  {
14047  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "' must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
14048  TrainDataVector.clear();
14049  Utilities->CallLogPop(2619);
14050  return(false);
14051  }
14052  else if((AVEntry.Command == "Sns") || (AVEntry.Command == "Sfs"))
14053  {
14054  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
14055  "Please make sure that the finish event of the service that links to this event is preceded by an "
14056  "event at the same location that has an identified location name, normally an arrival.");
14057  TrainDataVector.clear();
14058  Utilities->CallLogPop(2620);
14059  return(false);
14060  }
14061  else if(AVEntry.Command == "Snt-sh")
14062  {
14063  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
14064  "Please make sure that the service starts with zero speed and is at a named location.");
14065  TrainDataVector.clear();
14066  Utilities->CallLogPop(2623);
14067  return(false);
14068  }
14069  else if((AVEntry.Command == "Sns-fsh") || (AVEntry.Command == "Sns-sh"))
14070  {
14071  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
14072  "Please make sure that the event is followed (not necessarily immediately) by a departure.");
14073  TrainDataVector.clear();
14074  Utilities->CallLogPop(2622);
14075  return(false);
14076  }
14077  else if((AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"))
14078  {
14079  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
14080  TrainDataVector.clear();
14081  Utilities->CallLogPop(2621);
14082  return(false);
14083  }
14084  }
14085  }
14086  }
14087 
14088  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
14089  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14090  {
14091  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14092  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14093  {
14094  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14095  if((AVEntry.SequenceType == FinishSequence) && (AVEntry.Command != "F-nshs"))
14096  {
14097  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
14098  // i.e at least one more, must be a repeat
14099  {
14100  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
14101  {
14102  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish event for: " + TDEntry.HeadCode);
14103  TrainDataVector.clear();
14104  Utilities->CallLogPop(798);
14105  return(false);
14106  }
14107  }
14108  }
14109  if(AVEntry.Command == "F-nshs")
14110  {
14111  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
14112  // i.e has to be the last
14113  {
14114  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
14115  TrainDataVector.clear();
14116  Utilities->CallLogPop(1049);
14117  return(false);
14118  }
14119  }
14120  if(AVEntry.Command == "pas")
14121  {
14122  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14123  {
14124  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
14125  TrainDataVector.clear();
14126  Utilities->CallLogPop(1518);
14127  return(false);
14128  }
14129  }
14130  if(AVEntry.Command == "jbo")
14131  {
14132  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14133  {
14134  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
14135  TrainDataVector.clear();
14136  Utilities->CallLogPop(800);
14137  return(false);
14138  }
14139  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14140  if(!AtLocSuccessor(AVEntry2))
14141  {
14142  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
14143  ". The event isn't valid for a stationary train.");
14144  TrainDataVector.clear();
14145  Utilities->CallLogPop(801);
14146  return(false);
14147  }
14148  }
14149  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
14150  {
14151  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14152  {
14153  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
14154  TrainDataVector.clear();
14155  Utilities->CallLogPop(802);
14156  return(false);
14157  }
14158  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14159  if(!AtLocSuccessor(AVEntry2))
14160  {
14161  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
14162  ". The event isn't valid for a stationary train.");
14163  TrainDataVector.clear();
14164  Utilities->CallLogPop(803);
14165  return(false);
14166  }
14167  }
14168  if(AVEntry.Command == "cdt")
14169  {
14170  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14171  {
14172  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
14173  TrainDataVector.clear();
14174  Utilities->CallLogPop(804);
14175  return(false);
14176  }
14177  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14178  if(!AtLocSuccessor(AVEntry2))
14179  {
14180  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
14181  ". The event isn't valid for a stationary train.");
14182  TrainDataVector.clear();
14183  Utilities->CallLogPop(805);
14184  return(false);
14185  }
14186  }
14187  if(AVEntry.Command == "dsc")
14188  {
14189  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14190  {
14191  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' can't be the last event for: " + TDEntry.HeadCode);
14192  TrainDataVector.clear();
14193  Utilities->CallLogPop(2602);
14194  return(false);
14195  }
14196  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14197  if(!AtLocSuccessor(AVEntry2))
14198  {
14199  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' is followed by an illegal event for: " + TDEntry.HeadCode +
14200  ". The event isn't valid for a stationary train.");
14201  TrainDataVector.clear();
14202  Utilities->CallLogPop(2603);
14203  return(false);
14204  }
14205  }
14206  if(AVEntry.FormatType == TimeTimeLoc)
14207  {
14208  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14209  {
14210  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
14211  TrainDataVector.clear();
14212  Utilities->CallLogPop(806);
14213  return(false);
14214  }
14215  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14216  if(!MovingSuccessor(AVEntry2))
14217  {
14218  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
14219  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
14220  TrainDataVector.clear();
14221  Utilities->CallLogPop(807);
14222  return(false);
14223  }
14224  }
14225  if(AVEntry.FormatType == PassTime)
14226  {
14227  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14228  {
14229  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
14230  TrainDataVector.clear();
14231  Utilities->CallLogPop(1530);
14232  return(false);
14233  }
14234  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14235  if(!MovingSuccessor(AVEntry2))
14236  {
14237  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
14238  ". The event isn't valid for a moving train.");
14239  TrainDataVector.clear();
14240  Utilities->CallLogPop(1531);
14241  return(false);
14242  }
14243  }
14244  if(AVEntry.FormatType == Repeat)
14245  {
14246  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
14247  {
14248  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
14249  TrainDataVector.clear();
14250  Utilities->CallLogPop(808);
14251  return(false);
14252  }
14253  }
14254  }
14255  }
14256 
14257  // set arrival & departure times for TimeLocs & set their EventTimes to -1
14258  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14259  {
14260  bool LastEntryIsAnArrival = false;
14261  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14262  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
14263  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
14264  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14265  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
14266  {
14267  LastEntryIsAnArrival = false;
14268  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14269  {
14270  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14271  if(AVEntry.FormatType == TimeLoc)
14272  {
14273  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14274  {
14275  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14276  }
14277  if(LastEntryIsAnArrival)
14278  {
14279  AVEntry.DepartureTime = AVEntry.EventTime;
14280  AVEntry.EventTime = TDateTime(-1);
14281  LastEntryIsAnArrival = false;
14282  }
14283  else // last entry a departure
14284  {
14285  AVEntry.ArrivalTime = AVEntry.EventTime;
14286  AVEntry.EventTime = TDateTime(-1);
14287  LastEntryIsAnArrival = true;
14288  }
14289  }
14290  }
14291  }
14292  else // all others stopped at beginning
14293  {
14294  LastEntryIsAnArrival = true;
14295  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14296  {
14297  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14298  if(AVEntry.FormatType == TimeLoc)
14299  {
14300  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14301  {
14302  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14303  }
14304  if(LastEntryIsAnArrival)
14305  {
14306  AVEntry.DepartureTime = AVEntry.EventTime;
14307  AVEntry.EventTime = TDateTime(-1);
14308  LastEntryIsAnArrival = false;
14309  }
14310  else // last entry a departure
14311  {
14312  AVEntry.ArrivalTime = AVEntry.EventTime;
14313  AVEntry.EventTime = TDateTime(-1);
14314  LastEntryIsAnArrival = true;
14315  }
14316  }
14317  }
14318  }
14319  }
14320  // perform remaining successor checks for TimeLocs
14321  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14322  {
14323  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14324  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14325  {
14326  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14327  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
14328  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
14329  {
14330  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14331  {
14332  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
14333  TrainDataVector.clear();
14334  Utilities->CallLogPop(809);
14335  return(false);
14336  }
14337  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14338  if(!AtLocSuccessor(AVEntry2))
14339  {
14340  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
14341  ". The event isn't valid for a stationary train.");
14342  TrainDataVector.clear();
14343  Utilities->CallLogPop(810);
14344  return(false);
14345  }
14346  }
14347  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
14348  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
14349  {
14350  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14351  {
14352  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
14353  TrainDataVector.clear();
14354  Utilities->CallLogPop(811);
14355  return(false);
14356  }
14357  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14358  if(!MovingSuccessor(AVEntry2))
14359  {
14360  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
14361  ". The event isn't valid for a moving train.");
14362  TrainDataVector.clear();
14363  Utilities->CallLogPop(812);
14364  return(false);
14365  }
14366  }
14367  }
14368  }
14369 
14370  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
14371  // & repeats have no times set
14372  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14373  {
14374  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14375  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14376  {
14377  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14378  if(AVEntry.FormatType == TimeLoc)
14379  {
14380  if(AVEntry.EventTime != TDateTime(-1))
14381  {
14382  throw Exception("Timetable error, TimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14383  }
14384  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
14385  {
14386  throw Exception("Timetable error, TimeLoc event has neither arrival nor departure time set for " + TDEntry.HeadCode);
14387  }
14388  }
14389  if(AVEntry.FormatType == TimeTimeLoc)
14390  {
14391  if(AVEntry.EventTime != TDateTime(-1))
14392  {
14393  throw Exception("Timetable error, TimeTimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14394  }
14395  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
14396  {
14397  throw Exception("Timetable error, TimeTimeLoc event has either arrival or departure time not set for " + TDEntry.HeadCode);
14398  }
14399  }
14400  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
14401  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
14402  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
14403  {
14404  if(AVEntry.EventTime == TDateTime(-1))
14405  {
14406  throw Exception("Timetable error, Cmd or PassTime event has EventTime not set for " + TDEntry.HeadCode);
14407  }
14408  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14409  {
14410  throw Exception("Timetable error, Cmd or PassTime event has either arrival or departure time set for " + TDEntry.HeadCode);
14411  }
14412  }
14413  if(AVEntry.FormatType == Repeat)
14414  {
14415  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14416  {
14417  throw Exception("Timetable error, Repeat event has a time set for " + TDEntry.HeadCode);
14418  }
14419  }
14420  }
14421  }
14422 
14423  // check times stay same or increase, note that can have time of 0 if include midnight
14424  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14425  {
14426  TDateTime CurrentTime = TTClockTime; // the timetable start time
14427  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14428  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14429  {
14430  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14431  if(AVEntry.FormatType == Repeat)
14432  {
14433  break;
14434  }
14435  if(AVEntry.FormatType == FinRemHere)
14436  {
14437  break;
14438  }
14439  if(AVEntry.FormatType == TimeTimeLoc)
14440  {
14441  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
14442  {
14443  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
14444  TDEntry.HeadCode);
14445  TrainDataVector.clear();
14446  Utilities->CallLogPop(813);
14447  return(false);
14448  }
14449  if(AVEntry.ArrivalTime < CurrentTime)
14450  {
14451  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
14452  TDEntry.HeadCode);
14453  TrainDataVector.clear();
14454  Utilities->CallLogPop(814);
14455  return(false);
14456  }
14457  CurrentTime = AVEntry.DepartureTime;
14458  continue;
14459  }
14460  if(AVEntry.FormatType == TimeLoc)
14461  {
14462  if(AVEntry.ArrivalTime >= TDateTime(0))
14463  {
14464  if(AVEntry.ArrivalTime < CurrentTime)
14465  {
14466  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14467  TrainDataVector.clear();
14468  Utilities->CallLogPop(815);
14469  return(false);
14470  }
14471  CurrentTime = AVEntry.ArrivalTime;
14472  }
14473  else
14474  {
14475  if(AVEntry.DepartureTime < CurrentTime)
14476  // both may be 0 legitimately so must allow for this
14477  {
14478  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14479  TrainDataVector.clear();
14480  Utilities->CallLogPop(816);
14481  return(false);
14482  }
14483  CurrentTime = AVEntry.DepartureTime;
14484  }
14485  continue;
14486  }
14487  if(AVEntry.EventTime < CurrentTime)
14488  // all others have EventTime set
14489  {
14490  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
14491  ", may be before timetable start time");
14492  TrainDataVector.clear();
14493  Utilities->CallLogPop(835);
14494  return(false);
14495  }
14496  CurrentTime = AVEntry.EventTime;
14497  continue;
14498  }
14499  }
14500 
14501  // check locations consistent
14502  AnsiString LastLocationName = "";
14503 
14504  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14505  {
14506  bool LastEntryIsAnArrival = false;
14507  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14508  // first deal with moving Snt entries (all else stopped)
14509  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
14510  {
14511  LastEntryIsAnArrival = false;
14512  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
14513  if(LastLocationName != "")
14514  {
14515  throw Exception("Timetable error, moving Snt event has LocationName set for " + TDEntry.HeadCode);
14516  }
14517  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
14518  y++) // note that immediate successor to a moving Snt can only be a Moving type
14519  {
14520  // if it's a SignallerControl entry then the condition isn't met
14521  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14522  if(AVEntry.FormatType == Repeat)
14523  {
14524  break; // repeat = reached end (+allows repeat after signaller controlled entry)
14525  }
14526  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14527  {
14528  if(AVEntry.LocationName != LastLocationName)
14529  {
14530  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14531  AVEntry.Command);
14532  TrainDataVector.clear();
14533  Utilities->CallLogPop(823);
14534  return(false);
14535  }
14536  }
14537  else if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdDescription))
14538  // cdt is the only TimeCmd & dsc is the only TimeCmdDescription
14539  {
14540  if(AVEntry.LocationName != LastLocationName)
14541  {
14542  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14543  AVEntry.Command);
14544  TrainDataVector.clear();
14545  Utilities->CallLogPop(824);
14546  return(false);
14547  }
14548  }
14549  else if(AVEntry.FormatType == TimeTimeLoc)
14550  {
14551  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14552  // last entry must be a departure or would have failed earlier
14553  {
14554  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14555  TwoLocationFlag = true;
14556 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14557 // TwoOrMoreLocationsWarningGiven = true;
14558  }
14559  LastLocationName = AVEntry.LocationName;
14560  LastEntryIsAnArrival = false;
14561  }
14562  else if(AVEntry.FormatType == TimeLoc)
14563  {
14564  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14565  {
14566  SecondPassMessage(GiveMessages,
14567  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14568  TrainDataVector.clear();
14569  Utilities->CallLogPop(826);
14570  return(false);
14571  }
14572  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
14573  {
14574  SecondPassMessage(GiveMessages,
14575  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
14576  TrainDataVector.clear();
14577  Utilities->CallLogPop(827);
14578  return(false);
14579  }
14580  LastLocationName = AVEntry.LocationName;
14581  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14582  }
14583  }
14584  }
14585  else // all stationary starting entries
14586  {
14587  LastEntryIsAnArrival = true;
14588  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
14589  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14590  {
14591  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14592  if(AVEntry.FormatType == Repeat)
14593  {
14594  break;
14595  }
14596  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14597  // no need to add anything for shuttle starts since they are at loc (0) anyway
14598  {
14599  if(AVEntry.LocationName != LastLocationName)
14600  {
14601  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14602  AVEntry.Command);
14603  TrainDataVector.clear();
14604  Utilities->CallLogPop(828);
14605  return(false);
14606  }
14607  }
14608  else if(AVEntry.FormatType == TimeCmd)
14609  // cdt is the only TimeCmd
14610  {
14611  if(AVEntry.LocationName != LastLocationName)
14612  {
14613  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14614  AVEntry.Command);
14615  TrainDataVector.clear();
14616  Utilities->CallLogPop(829);
14617  return(false);
14618  }
14619  }
14620  else if(AVEntry.FormatType == TimeTimeLoc)
14621  {
14622  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14623  // last entry must be a departure or would have failed earlier
14624  {
14625  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14626  TwoLocationFlag = true;
14627 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14628 // TwoOrMoreLocationsWarningGiven = true;
14629  }
14630  LastLocationName = AVEntry.LocationName;
14631  LastEntryIsAnArrival = false;
14632  }
14633  else if(AVEntry.FormatType == TimeLoc)
14634  {
14635  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14636  {
14637  SecondPassMessage(GiveMessages,
14638  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14639  TrainDataVector.clear();
14640  Utilities->CallLogPop(831);
14641  return(false);
14642  }
14643  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
14644  {
14645  SecondPassMessage(GiveMessages,
14646  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
14648 // TrainDataVector.clear();
14649 // Utilities->CallLogPop(832);
14650 // return false;
14651  }
14652  LastLocationName = AVEntry.LocationName;
14653  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14654  }
14655  }
14656  }
14657  }
14658 
14659  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
14660  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
14661  AnsiString LocationNameToBeChecked = "";
14662 
14663  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14664  {
14665  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14666  unsigned int y = 0;
14667  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14668  // first discard unlocated Snt entries as they don't have location name set
14669  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14670  {
14671  y = 1;
14672  }
14673  while(y < TDEntry.ActionVector.size())
14674  // need to check each location name separately in turn, skipped for SignallerControl entries
14675  {
14676  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
14677  {
14678  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14679  }
14680  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
14681  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
14682  {
14683  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
14684  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
14685  {
14686  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14687  }
14688  if(AVEntry.Command == "cdt")
14689  {
14690  break; // out of the 'z' loop since the check is only valid up to a change of direction
14691  }
14692  if(AVEntry.LocationName == LocationNameToBeChecked)
14693  {
14694  continue; // keep going while name same
14695  }
14696  if(AVEntry.LocationName != LocationNameToBeChecked)
14697  // if name different check forwards to see if repeats
14698  {
14699  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
14700  {
14701  if(TDEntry.ActionVector.at(a).Command == "cdt")
14702  {
14703  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
14704  }
14705  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14706  {
14707  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14708  TwoLocationFlag = true;
14709 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14710 // TwoOrMoreLocationsWarningGiven = true;
14711  }
14712  }
14713  break; // out of the 'z' loop since have checked 'a' as far as need to
14714  }
14715  }
14716  y++;
14717  }
14718  }
14719  if(TwoLocationFlag) //messages for this moved to InterfaceUnit - separate box listing potential errors and asking user to check
14720  {
14721  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
14722  TwoLocationList.unique(); //remove duplicates
14723  }
14724 
14725  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
14726  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14727  {
14728  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14729  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14730  {
14731  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14732  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
14733  {
14734  throw Exception("Error, non- 'Snt', 'Fer' or Repeat event doesn't have a location name set for " + TDEntry.HeadCode);
14735  }
14736  AnsiString LocName = "";
14737  // dummy, only used so can call IsSNTEntryLocated
14738  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
14739  {
14740  if(AVEntry.LocationName == "")
14741  {
14742  throw Exception("Error, 'Snt' event at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
14743  }
14744  }
14745  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
14746  {
14747  if(AVEntry.LocationName != "")
14748  {
14749  throw Exception("Error, 'Snt' unlocated event has a location name set for " + TDEntry.HeadCode);
14750  }
14751  }
14752  }
14753  }
14754 
14755 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
14756  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
14757  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
14758 
14759  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
14760  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
14761 
14762  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
14763  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
14764  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
14765 */
14766  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
14767  {
14768  // non-shuttles & non-repeating links separately, but don't check that there isn't a
14769  // duplicate between a non-repeating shuttle and another - leave original tests in as
14770  // these also set the pointers
14771  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14772  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14773  {
14774  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14775  if(AVEntry.OtherHeadCode != "")
14776  {
14777  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
14778  {
14779  Utilities->CallLogPop(1584);
14780  return(false); // error message given in called function
14781  }
14782  }
14783  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14784  {
14785  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
14786  {
14787  Utilities->CallLogPop(1585);
14788  return(false); // error message given in called function
14789  }
14790  }
14791  }
14792  }
14793 
14794  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14795  {
14796  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14797  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14798  {
14799  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14800  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14801  {
14802  if(AVEntry.OtherHeadCode != "")
14803  {
14804  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, true, GiveMessages))
14805  // false = non-shuttle
14806  {
14807  Utilities->CallLogPop(864);
14808  return(false); // error message given in called function
14809  }
14810  }
14811  }
14812  }
14813  }
14814 
14815  // now repeat the check just for the shuttles
14816  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14817  {
14818  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14819  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14820  {
14821  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14822  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
14823  {
14824  if(AVEntry.OtherHeadCode != "")
14825  {
14826  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, true, GiveMessages))
14827  // true = shuttle
14828  {
14829  Utilities->CallLogPop(1100);
14830  return(false); // error message given in called function
14831  }
14832  }
14833  }
14834  }
14835  }
14836 
14837  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14838  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14839  {
14840  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14841  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14842  {
14843  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14844  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14845  {
14846  if(!CheckNonRepeatingShuttleLinksAndSetData(0, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, true, GiveMessages))
14847  {
14848  Utilities->CallLogPop(1060);
14849  return(false); // error message given in called function
14850  }
14851  }
14852  }
14853  }
14854 
14855  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14856  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14857  // don't ever need to and as designed would skip repeats
14858  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14859  {
14860  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14861  {
14862  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14863  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
14864  {
14865  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
14866  {
14867  Utilities->CallLogPop(1090);
14868  return(false); // error message given in called function
14869  }
14870  }
14871  }
14872  }
14873 
14874  // check all entries have all types set to something
14875  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14876  {
14877  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14878  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14879  {
14880  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14881  if(AVEntry.FormatType == NoFormat)
14882  {
14883  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
14884  }
14885  else if(AVEntry.SequenceType == NoSequence)
14886  {
14887  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
14888  }
14889  else if(AVEntry.LocationType == NoLocation)
14890  {
14891  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
14892  }
14893  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
14894  {
14895  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
14896  }
14897  }
14898  }
14899 
14900  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
14901  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14902  {
14903  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14904  // non-const reference so can alter content
14905  TTrainOperatingData TData;
14906  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14907  if(LastAVEntry.FormatType == Repeat) // check if a repeat
14908  {
14909 /*
14910  class TTrainOperatingData
14911  {
14912  public:
14913  int TrainID; - default, set at construction
14914  TActionEventType EventReported; used during operation
14915  TRunningEntry RunningEntry; - default, set at construction
14916  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
14917  };
14918 */
14919  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
14920  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14921  {
14922  TDEntry.TrainOperatingDataVector.push_back(TData);
14923  }
14924  }
14925  else
14926  {
14927  TDEntry.NumberOfTrains = 1;
14928  }
14929  }
14930 
14931  // check that don't include any Continuation names
14932  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14933  {
14934  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14935  {
14936  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
14937  AnsiString HC = TrainDataVector.at(x).HeadCode;
14938  if(LocName != "")
14939  {
14940  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
14941  {
14942  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
14943  TrainDataVector.clear();
14944  Utilities->CallLogPop(1578);
14945  return(false);
14946  }
14947  }
14948  }
14949  }
14950 
14951  // check that all repeat times below 96h
14952  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14953  {
14954  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
14955  int IncMinutes = 0;
14956  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
14957  {
14958  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
14959  }
14960  else
14961  {
14962  continue; // basic times already checked in CheckTimeValidity
14963  }
14964  AnsiString HC = TrainDataVector.at(x).HeadCode;
14965  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14966  {
14967  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
14968  {
14969  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14970  {
14971  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
14972  TrainDataVector.clear();
14973  Utilities->CallLogPop(1818);
14974  return(false);
14975  }
14976  }
14977  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
14978  {
14979  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14980  // 3d 23h 59m = 3.9993055556
14981  {
14982  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14983  TrainDataVector.clear();
14984  Utilities->CallLogPop(1819);
14985  return(false);
14986  }
14987  }
14988  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
14989  {
14990  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14991  // 3d 23h 59m = 3.9993055556
14992  {
14993  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14994  TrainDataVector.clear();
14995  Utilities->CallLogPop(1820);
14996  return(false);
14997  }
14998  }
14999  }
15000  }
15001 
15002  // Now that all set up change any extended headcodes back to ordinary headcodes
15003  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15004  {
15005  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
15006  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15007  {
15008  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
15009  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
15010  }
15011  }
15012 
15013  // SaveTrainDataVectorToFile(0);//for testing purposes
15015  Utilities->CallLogPop(782);
15016  return(true);
15017 }
15018 
15019 // ---------------------------------------------------------------------------
15020 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
15022 {
15023  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
15024 }
15025 
15026 // ---------------------------------------------------------------------------
15027 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
15029 {
15030  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
15031  (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
15032  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
15033 }
15034 
15035 // ---------------------------------------------------------------------------
15036 
15037 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
15038 {
15039  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
15040  if(HeadCode.Length() > 4) // ignore otherwise
15041  {
15042  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
15043  }
15044  Utilities->CallLogPop(1593);
15045 }
15046 
15047 // ---------------------------------------------------------------------------
15048 
15049 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
15050 {
15051  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
15052  SecondHeadCode);
15053  int ForwardCount = 0;
15054  int ReverseCount = 0;
15055 
15056  if(MainHeadCode == SecondHeadCode)
15057  {
15058  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
15059  TrainDataVector.clear();
15060  Utilities->CallLogPop(1594);
15061  return(false);
15062  }
15063  // forward check
15064  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15065  {
15066  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15067  if(TDEntry.HeadCode == MainHeadCode)
15068  {
15069  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15070  {
15071  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15072  if(AVEntry.OtherHeadCode == SecondHeadCode)
15073  {
15074  ForwardCount++;
15075  }
15076  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
15077  // need own check in case both 'Other' & 'NonRepeating' have same headcode
15078  {
15079  ForwardCount++;
15080  }
15081  }
15082  }
15083  }
15084  if(ForwardCount == 0)
15085  // this is an exception because the headcodes are selected in the same order as the forward check
15086  {
15087  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
15088  }
15089  if(ForwardCount > 2)
15090  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
15091  {
15092  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
15093  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
15094  TrainDataVector.clear();
15095  Utilities->CallLogPop(1587);
15096  return(false);
15097  }
15098  // reverse check
15099  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15100  {
15101  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15102  if(TDEntry.HeadCode == SecondHeadCode)
15103  {
15104  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15105  {
15106  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15107  if(AVEntry.OtherHeadCode == MainHeadCode)
15108  {
15109  ReverseCount++;
15110  }
15111  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
15112  {
15113  ReverseCount++;
15114  }
15115  }
15116  }
15117  }
15118 
15119  if(ReverseCount == 0)
15120  {
15121  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
15122  TrainDataVector.clear();
15123  Utilities->CallLogPop(1588);
15124  return(false);
15125  }
15126  if(ReverseCount > 2)
15127  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
15128  {
15129  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
15130  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
15131  TrainDataVector.clear();
15132  Utilities->CallLogPop(1589);
15133  return(false);
15134  }
15135  if(ForwardCount != ReverseCount)
15136  {
15137  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
15138  " than the other way round");
15139  TrainDataVector.clear();
15140  Utilities->CallLogPop(1610);
15141  return(false);
15142  }
15143  Utilities->CallLogPop(1590);
15144  return(true);
15145 }
15146 
15147 // ---------------------------------------------------------------------------
15148 
15149 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
15150 /* Return false for no find or more than one find, check correct types of link
15151  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
15152  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
15153  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
15154  Then do the same in reverse.
15155  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
15156  if main is Fns other must be Sns; if main is jbo other must be Fjo.
15157  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
15158  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
15159  for Sfs & Sns services. Finally check the repeat entries if present are consistent
15160 
15161  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
15162  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
15163 
15164  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
15165 */
15166 
15167 {
15168  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
15169  int ForwardCount = 0;
15170  int ReverseCount = 0;
15171  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15172  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15173  TTrainDataEntry *MainTrainDataPtr = 0;
15174  TTrainDataEntry *OtherTrainDataPtr = 0;
15175 
15176  // forward check
15177  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15178  {
15179  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15180  if(TDEntry.HeadCode == MainHeadCode)
15181  {
15182  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15183  {
15184  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15185  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15186  {
15187  if(AVEntry.OtherHeadCode == OtherHeadCode)
15188  {
15189  MainTrainDataPtr = &TrainDataVector.at(x);
15190  ForwardEntryPtr = &AVEntry;
15191  ForwardCount++;
15192  ForwardTDVectorNumber = x;
15193  }
15194  }
15195  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
15196  (AVEntry.Command == "Frh-sh")))
15197  {
15198  if(AVEntry.OtherHeadCode == OtherHeadCode)
15199  {
15200  MainTrainDataPtr = &TrainDataVector.at(x);
15201  ForwardEntryPtr = &AVEntry;
15202  ForwardCount++;
15203  ForwardTDVectorNumber = x;
15204  }
15205  }
15206  }
15207  }
15208  }
15209  if(ForwardCount == 0)
15210  // this is an exception because the headcodes are selected in the same order as the forward check
15211  {
15212  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
15213  }
15214  if(ForwardCount > 1)
15215  {
15216  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
15217  MainHeadCode);
15218  TrainDataVector.clear();
15219  Utilities->CallLogPop(836);
15220  return(false);
15221  }
15222  // reverse check
15223  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15224  {
15225  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15226  if(TDEntry.HeadCode == OtherHeadCode)
15227  {
15228  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15229  {
15230  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15231  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15232  {
15233  if(AVEntry.OtherHeadCode == MainHeadCode)
15234  {
15235  OtherTrainDataPtr = &TrainDataVector.at(x);
15236  ReverseCount++;
15237  ReverseEntryPtr = &AVEntry;
15238  ReverseTDVectorNumber = x;
15239  }
15240  }
15241  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
15242  {
15243  if(AVEntry.OtherHeadCode == MainHeadCode)
15244  {
15245  OtherTrainDataPtr = &TrainDataVector.at(x);
15246  ReverseCount++;
15247  ReverseEntryPtr = &AVEntry;
15248  ReverseTDVectorNumber = x;
15249  }
15250  }
15251  }
15252  }
15253  }
15254 
15255  if(ReverseCount == 0)
15256  {
15257  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
15258  TrainDataVector.clear();
15259  Utilities->CallLogPop(837);
15260  return(false);
15261  }
15262  if(ReverseCount > 1)
15263  {
15264  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15265  OtherHeadCode);
15266  TrainDataVector.clear();
15267  Utilities->CallLogPop(838);
15268  return(false);
15269  }
15270  // these will all be false for !Shuttle
15271  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
15272  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
15273  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
15274  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
15275 
15276  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
15277  {
15278  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
15279  TrainDataVector.clear();
15280  Utilities->CallLogPop(1058);
15281  return(false);
15282  }
15283  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
15284  {
15285  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
15286  TrainDataVector.clear();
15287  Utilities->CallLogPop(1059);
15288  return(false);
15289  }
15290  if(SetDataAndCheckLocations)
15291  {
15292  if(ForwardEntryPtr->LocationName == "")
15293  {
15294  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15295  ". One or other service does not have a location set");
15296  TrainDataVector.clear();
15297  Utilities->CallLogPop(526);
15298  return(false);
15299  }
15300  if(ReverseEntryPtr->LocationName == "")
15301  {
15302  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15303  ". One or other service does not have a location set");
15304  TrainDataVector.clear();
15305  Utilities->CallLogPop(527);
15306  return(false);
15307  }
15308  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15309  {
15310  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15311  " is at a different location to the referencing train " + MainHeadCode);
15312  TrainDataVector.clear();
15313  Utilities->CallLogPop(842);
15314  return(false);
15315  }
15316  }
15317  // ignore shuttle repeat links for first time check
15318  if(!Shuttle)
15319  {
15320  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15321  {
15322  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15323  " has a different event time to the referencing train " + MainHeadCode);
15324  TrainDataVector.clear();
15325  Utilities->CallLogPop(525);
15326  return(false);
15327  }
15328  }
15329  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
15330  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
15331  if(ForwardShuttleStart && ReverseShuttleFinish)
15332  // Shuttle must be true if these are true
15333  {
15334  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
15335  {
15336  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
15337  " first repeat restart time not consistent with finish service " + OtherHeadCode);
15338  TrainDataVector.clear();
15339  Utilities->CallLogPop(1055);
15340  return(false);
15341  }
15342  }
15343  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
15344  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
15345  {
15346  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15347  {
15348  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
15349  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15350  TrainDataVector.clear();
15351  Utilities->CallLogPop(528);
15352  return(false);
15353  }
15354  }
15355  if(ReverseEntryPtr->Command == "Fjo")
15356  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
15357  {
15358  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15359  {
15360  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
15361  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15362  TrainDataVector.clear();
15363  Utilities->CallLogPop(862);
15364  return(false);
15365  }
15366  }
15367  if(ReverseEntryPtr->Command == "Fns")
15368  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
15369  {
15370  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15371  {
15372  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
15373  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15374  TrainDataVector.clear();
15375  Utilities->CallLogPop(529);
15376  return(false);
15377  }
15378  }
15379  if(ForwardEntryPtr->Command == "Sfs")
15380  {
15381  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
15382  {
15383  SecondPassMessage(GiveMessages,
15384  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
15385  MainHeadCode);
15386  TrainDataVector.clear();
15387  Utilities->CallLogPop(530);
15388  return(false);
15389  }
15390  }
15391  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
15392  {
15393  if(ReverseEntryPtr->Command != "Sfs")
15394  {
15395  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
15396  MainHeadCode);
15397  TrainDataVector.clear();
15398  Utilities->CallLogPop(839);
15399  return(false);
15400  }
15401  else
15402  {
15403  if(SetDataAndCheckLocations)
15404  {
15405  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
15406  {
15407  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
15408  TrainDataVector.clear();
15409  Utilities->CallLogPop(849);
15410  return(false);
15411  }
15412  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
15413  {
15414  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
15415  TrainDataVector.clear();
15416  Utilities->CallLogPop(850);
15417  return(false);
15418  }
15419  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15420  {
15421  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15422  TrainDataVector.clear();
15423  Utilities->CallLogPop(846);
15424  return(false);
15425  }
15426  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15427  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15428  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15429  {
15430  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15431  }
15432  }
15433  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
15434  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15435  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15436  }
15437  }
15438  if(ForwardEntryPtr->Command == "Sns")
15439  {
15440  if(ReverseEntryPtr->Command != "Fns")
15441  {
15442  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
15443  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
15444  TrainDataVector.clear();
15445  Utilities->CallLogPop(531);
15446  return(false);
15447  }
15448  }
15449  if(ForwardEntryPtr->Command == "Fns")
15450  {
15451  if(ReverseEntryPtr->Command != "Sns")
15452  {
15453  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
15454  " and forms a new service with headcode " + OtherHeadCode);
15455  TrainDataVector.clear();
15456  Utilities->CallLogPop(840);
15457  return(false);
15458  }
15459  else
15460  {
15461  if(SetDataAndCheckLocations)
15462  {
15463  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15464  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15465  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15466  {
15467  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15468  }
15469  }
15470  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15471  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15472  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15473  }
15474  }
15475  if(ForwardEntryPtr->Command == "jbo")
15476  {
15477  if(ReverseEntryPtr->Command != "Fjo")
15478  {
15479  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
15480  " and is joined by a train with headcode " + OtherHeadCode);
15481  TrainDataVector.clear();
15482  Utilities->CallLogPop(841);
15483  return(false);
15484  }
15485  }
15486  if(ForwardEntryPtr->Command == "Fjo")
15487  {
15488  if(ReverseEntryPtr->Command != "jbo")
15489  {
15490  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
15491  " and joins a train with headcode " + OtherHeadCode);
15492  TrainDataVector.clear();
15493  Utilities->CallLogPop(532);
15494  return(false);
15495  }
15496  else
15497  {
15498  if(SetDataAndCheckLocations)
15499  {
15500  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15501  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15502  }
15503 /*
15504  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
15505  {
15506  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed; //this should only be set when the trains join
15507  } not when internal timetable being compiled. Dropped at v2.15.0 when Brent Mackie reported error on 18/02/23 via discord
15508 
15509  //added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a
15510  //zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
15511  //notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the
15512  //'joined by' train's max speed is less.
15513 */
15514  }
15515  }
15516  if(ForwardShuttleStart)
15517  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
15518  {
15519  if(!ReverseShuttleFinish)
15520  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
15521  {
15522  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
15523  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
15524  TrainDataVector.clear();
15525  Utilities->CallLogPop(1056);
15526  return(false);
15527  }
15528  }
15529  if(ReverseShuttleStart)
15530  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
15531  {
15532  if(!ForwardShuttleFinish)
15533  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
15534  {
15535  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
15536  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
15537  TrainDataVector.clear();
15538  Utilities->CallLogPop(1057);
15539  return(false);
15540  }
15541  else
15542  {
15543  if(SetDataAndCheckLocations)
15544  {
15545  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15546  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15547  }
15548 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
15549  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
15550 */
15551  }
15552  }
15553  // check repeat information consistent if present
15554  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
15555  // and those not accessed here
15556 
15557  // still need to check the non-repeating links and that they have no repeats - do that outside this function
15558  bool MainRepeat = false, OtherRepeat = false;
15559  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
15560 
15561  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15562  {
15563  MainRepeat = true;
15564  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
15565  }
15566  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15567  {
15568  OtherRepeat = true;
15569  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
15570  }
15571  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
15572  {
15573  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
15574  " and the associated train with headcode " + OtherHeadCode);
15575  TrainDataVector.clear();
15576  Utilities->CallLogPop(844);
15577  return(false);
15578  }
15579  if(MainRepeat && OtherRepeat)
15580  {
15581  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
15582  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
15583  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
15584  {
15585  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
15586  " and the associated train with headcode " + OtherHeadCode);
15587  TrainDataVector.clear();
15588  Utilities->CallLogPop(845);
15589  return(false);
15590  }
15591  }
15592  Utilities->CallLogPop(863);
15593  return(true);
15594 }
15595 
15596 // ---------------------------------------------------------------------------
15597 
15598 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
15599 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
15600 {
15601  // strip spaces from extreme ends of input
15602  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
15603  if(Input == "")
15604  {
15605  Utilities->CallLogPop(856);
15606  return;
15607  }
15608  while(Input[1] == ' ')
15609  {
15610  if(Input.Length() > 1)
15611  {
15612  Input = Input.SubString(2, Input.Length() - 1);
15613  }
15614  else
15615  {
15616  Input = "";
15617  Utilities->CallLogPop(857);
15618  return;
15619  }
15620  }
15621  if(Input == "")
15622  {
15623  Utilities->CallLogPop(858);
15624  return;
15625  }
15626  while(Input[Input.Length()] == ' ')
15627  {
15628  if(Input.Length() > 1)
15629  {
15630  Input = Input.SubString(1, Input.Length() - 1);
15631  }
15632  else
15633  {
15634  Input = "";
15635  Utilities->CallLogPop(859);
15636  return;
15637  }
15638  }
15639  // now strip spaces immediately after all commas and semicolons within the text
15640  AnsiString Output = "";
15641  bool DelimiterFound = false;
15642 
15643  for(int x = 1; x < Input.Length() + 1; x++)
15644  {
15645  if(DelimiterFound)
15646  {
15647  if(Input[x] == ' ')
15648  {
15649  continue;
15650  }
15651  }
15652  if((Input[x] != ',') && (Input[x] != ';'))
15653  {
15654  DelimiterFound = false;
15655  Output = Output + Input[x];
15656  }
15657  else
15658  {
15659  DelimiterFound = true;
15660  Output = Output + Input[x];
15661  }
15662  }
15663  if(Output == "")
15664  {
15665  Input = "";
15666  Utilities->CallLogPop(860);
15667  return;
15668  }
15669  // now strip spaces immediately before all commas and semicolons within the text
15670  Input = Output;
15671  Output = "";
15672  DelimiterFound = false;
15673  for(int x = Input.Length(); x > 0; x--)
15674  {
15675  if(DelimiterFound)
15676  {
15677  if(Input[x] == ' ')
15678  {
15679  continue;
15680  }
15681  }
15682  if((Input[x] != ',') && (Input[x] != ';'))
15683  {
15684  DelimiterFound = false;
15685  Output = AnsiString(Input[x]) + Output;
15686  }
15687  else
15688  {
15689  DelimiterFound = true;
15690  Output = AnsiString(Input[x]) + Output;
15691  }
15692  }
15693  Input = Output;
15694  Utilities->CallLogPop(861);
15695 }
15696 
15697 // ---------------------------------------------------------------------------
15698 
15699 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName) //this is the original version re-instated at v2.15.0 Beta6
15700 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
15701 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
15702 // a signaller control entry & speed is zero or it is followed immediately by Frh, Fjo, Fns or F-nshs (allows empty stock pickup).
15703 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
15704 // are done in this function, they must be done elsewhere.
15705 //a starting speed > 0 always returns false
15706 {
15707  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
15708  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
15709  LocationName = "";
15710  if(TDEntry.StartSpeed > 0)
15711  {
15712  Utilities->CallLogPop(1784);
15713  return(false);
15714  }
15715  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
15716  {
15717  throw Exception("Error, first event not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
15718  }
15720  {
15721  Utilities->CallLogPop(852);
15722  return(false);
15723  }
15724  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
15725  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
15726 
15727  if(LocRear != "")
15728  {
15729  LocationName = LocRear;
15730  }
15731  else
15732  {
15733  LocationName = LocFront;
15734  }
15735  if(LocationName == "")
15736  {
15737  Utilities->CallLogPop(1036);
15738  return(false);
15739  }
15740  if(AVEntry0.SignallerControl)
15741  {
15742  Utilities->CallLogPop(1773);
15743  return(true);
15744  }
15745 // here if not a signaller start entry so must be at least one more entry, and it must be at the same location as the Snt to be located
15746 
15747 //Ok Not ok continue
15748 
15749 //Frh if Snt Frh-sh cdt
15750 //Fns if Snt Fns-sh fsp or rsp
15751 //Fjo if Snt TimeTimeLoc jbo
15752 //F-nshs if Snt pas dsc
15753 //TimeLoc dep Fer
15754 // TimeLoc arr
15755 
15756  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
15757  {
15758  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
15759  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
15760  {
15761  Utilities->CallLogPop(1037);
15762  return(true);
15763  }
15764  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
15765  {
15766  Utilities->CallLogPop(2442);
15767  return(true);
15768  }
15769  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
15770  {
15771  Utilities->CallLogPop(2438);
15772  return(false);
15773  }
15774  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
15775  {
15776  Utilities->CallLogPop(854);
15777  return(false);
15778  }
15779  if((AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
15780  {
15781  continue;
15782  }
15783  }
15784  Utilities->CallLogPop(855);
15785  return(false);
15786 
15787 }
15788 
15789 // ---------------------------------------------------------------------------
15790 
15791 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
15792 {
15793  // checks that the new train start elements are valid - both exist & are connected, and that not
15794  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
15795  // & not starting with front on a continuation
15796  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
15797  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
15798 
15799  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
15800  if(RearPosition < 0)
15801  // error message given in GetTrackVectorPositionFromString
15802  {
15803  Utilities->CallLogPop(759);
15804  return(false);
15805  }
15806  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
15807  if(FrontPosition < 0)
15808  // error message given in GetTrackVectorPositionFromString
15809  {
15810  Utilities->CallLogPop(760);
15811  return(false);
15812  }
15813  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
15814  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
15815  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
15816 
15817  // check front & rear connected
15818  for(int x = 0; x < 4; x++)
15819  {
15820  if(RearTrackElement.Conn[x] == FrontPosition)
15821  {
15822  RearExitPos = x;
15823  break;
15824  }
15825  if(x == 3)
15826  {
15827  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
15828  Utilities->CallLogPop(762);
15829  return(false);
15830  }
15831  }
15832  // check not starting with front on a continuation
15833  if(FrontType == Continuation)
15834  {
15835  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
15836  Utilities->CallLogPop(937);
15837  return(false);
15838  }
15839  // check not starting on a level crossing
15840  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
15841  {
15842  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
15843  Utilities->CallLogPop(1951);
15844  return(false);
15845  }
15846  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
15847  {
15848  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
15849  Utilities->CallLogPop(1952);
15850  return(false);
15851  }
15852  // check if trying to start on diverging leg of points
15853  if((RearType == Points) && (RearExitPos == 3))
15854  {
15855  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
15856  Utilities->CallLogPop(936);
15857  return(false);
15858  }
15859  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
15860  {
15861  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
15862  Utilities->CallLogPop(1808);
15863  return(false);
15864  }
15865  Utilities->CallLogPop(905);
15866  return(true);
15867 }
15868 
15869 // ---------------------------------------------------------------------------
15870 
15871 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
15872 // Rear & front element validity already checked in CheckStartPositionValidity
15873 // This checks for points in correct orientation, no train at start position and not starting on a locked route
15874 {
15875  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
15876  AnsiString(RearExitPos));
15877  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
15878 
15879  if(RearTrackElement.TrackType == Continuation)
15880  {
15881  EventType = FailTrainEntry;
15882  }
15883  else
15884  {
15885  EventType = FailCreateTrain;
15886  }
15887  int FrontPosition = RearTrackElement.Conn[RearExitPos];
15888  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
15889  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
15890  TTrackType RearType = RearTrackElement.TrackType;
15891  TTrackType FrontType = FrontTrackElement.TrackType;
15892  AnsiString RearName, FrontName;
15893 
15894  if(RearTrackElement.ActiveTrackElementName != "")
15895  {
15896  RearName = RearTrackElement.ActiveTrackElementName;
15897  }
15898  else
15899  {
15900  RearName = RearTrackElement.ElementID;
15901  }
15902  if(FrontTrackElement.ActiveTrackElementName != "")
15903  {
15904  FrontName = FrontTrackElement.ActiveTrackElementName;
15905  }
15906  else
15907  {
15908  FrontName = FrontTrackElement.ElementID;
15909  }
15910  TPrefDirElement PrefDirElement; // needed for next function but not used
15911  int LockedVectorNumber; // needed for next function but not used
15912 
15913  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
15914  {
15915  if(ReportFlag)
15916  {
15917  if(EventType == FailCreateTrain)
15918  {
15919  EventType = FailCreateLockedRoute;
15920  }
15921  else
15922  {
15923  EventType = FailEnterLockedRoute;
15924  }
15925  LogActionError(47, HeadCode, "", EventType, FrontName);
15926  }
15927  Utilities->CallLogPop(940);
15928  return(false);
15929  }
15930  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
15931  {
15932  if(ReportFlag)
15933  {
15934  if(EventType == FailCreateTrain)
15935  {
15936  EventType = FailCreateLockedRoute;
15937  }
15938  else
15939  {
15940  EventType = FailEnterLockedRoute;
15941  }
15942  LogActionError(48, HeadCode, "", EventType, RearName);
15943  }
15944  Utilities->CallLogPop(1809);
15945  return(false);
15946  }
15947  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
15948  {
15949  if(ReportFlag)
15950  {
15951  LogActionError(27, HeadCode, "", EventType, RearName);
15952  }
15953  Utilities->CallLogPop(1810);
15954  return(false);
15955  }
15956  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
15957  {
15958  if(ReportFlag)
15959  {
15960  if(EventType == FailCreateTrain)
15961  {
15962  LogActionError(28, HeadCode, "", EventType, FrontName);
15963  }
15964  else
15965  {
15966  LogActionError(43, HeadCode, "", EventType, RearName);
15967  }
15968  }
15969  Utilities->CallLogPop(941);
15970  return(false);
15971  }
15972  if(RearType == Bridge)
15973  {
15974  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15975  {
15976  if(ReportFlag)
15977  {
15978  LogActionError(29, HeadCode, "", EventType, RearName);
15979  }
15980  Utilities->CallLogPop(942);
15981  return(false);
15982  }
15983  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15984  {
15985  if(ReportFlag)
15986  {
15987  LogActionError(30, HeadCode, "", EventType, RearName);
15988  }
15989  Utilities->CallLogPop(943);
15990  return(false);
15991  }
15992  }
15993  if(FrontType == Bridge)
15994  {
15995  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15996  {
15997  if(ReportFlag)
15998  {
15999  if(EventType == FailCreateTrain)
16000  {
16001  LogActionError(31, HeadCode, "", EventType, FrontName);
16002  }
16003  else
16004  {
16005  LogActionError(44, HeadCode, "", EventType, RearName);
16006  }
16007  }
16008  Utilities->CallLogPop(944);
16009  return(false);
16010  }
16011  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
16012  {
16013  if(ReportFlag)
16014  {
16015  if(EventType == FailCreateTrain)
16016  {
16017  LogActionError(45, HeadCode, "", EventType, FrontName);
16018  }
16019  else
16020  {
16021  LogActionError(46, HeadCode, "", EventType, RearName);
16022  }
16023  }
16024  Utilities->CallLogPop(945);
16025  return(false);
16026  }
16027  }
16028  EventType = FailCreatePoints;
16029  if(RearType == Points)
16030  {
16031  if(RearTrackElement.Attribute == 1)
16032  {
16033  if(ReportFlag)
16034  {
16035  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
16036  }
16037  Utilities->CallLogPop(933);
16038  return(false);
16039  }
16040  }
16041  if(FrontType == Points)
16042  {
16043  if(FrontTrackElement.Attribute == 1)
16044  {
16045  if(ReportFlag)
16046  {
16047  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
16048  }
16049  Utilities->CallLogPop(934);
16050  return(false);
16051  }
16052  }
16053 
16054  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
16055  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
16056  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
16057  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
16058  int RouteNumber; //not used
16059  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
16060  {
16061  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
16062  {
16063  EventType = FailEntryRouteSetAgainst;
16064  if(ReportFlag)
16065  {
16066  LogActionError(63, HeadCode, "", EventType, RearName);
16067  }
16068  Utilities->CallLogPop(2317);
16069  return(false);
16070  }
16071  }
16072  Utilities->CallLogPop(939);
16073  return(true);
16074 }
16075 
16076 // ---------------------------------------------------------------------------
16077 
16078 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
16079 {
16080  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
16081  "," + AnsiString(IncDigits));
16082  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
16083  {
16084  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
16085  }
16086  if(!Last2CharactersBothDigits(2, BaseHeadCode))
16087  {
16088  Utilities->CallLogPop(1893);
16089  return(BaseHeadCode);
16090  }
16091  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
16092  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
16093 
16094  while(NextRepeatDigits >= 100)
16095  {
16096  NextRepeatDigits -= 100; // rolls over after 99
16097  }
16098  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
16099 
16100  if(NextRepeatDigitsStr.Length() < 2)
16101  {
16102  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
16103  }
16104  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
16105 
16106  Utilities->CallLogPop(1365);
16107  return(NextRepeatHeadCode);
16108 }
16109 
16110 // ---------------------------------------------------------------------------
16111 
16112 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
16113 {
16114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
16115  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
16116  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
16117  Utilities->CallLogPop(1366);
16118  return(NextRepeatTime);
16119 }
16120 
16121 // ---------------------------------------------------------------------------
16122 
16123 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
16124 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
16125 {
16126  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
16127  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
16128  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16129  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16130  int RepeatSecs = RepeatMinutes * 60;
16131 
16132  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
16133  {
16134  Utilities->CallLogPop(1367);
16135  return(false);
16136  }
16137  else
16138  {
16139  Utilities->CallLogPop(1368);
16140  return(true);
16141  }
16142 }
16143 
16144 // ---------------------------------------------------------------------------
16145 
16146 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
16147 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent, set links if SetDataAndCheckLocations true
16148 
16149 /* Double crosslink (shuttle) table:
16150 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16151  Code ShuttleLink- EntryPtr ShuttleLink-
16152  HeadCode EntryPtr
16153 
16154 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16155 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16156 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16157 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16158 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16159 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16160 
16161 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16162 */
16163 
16164 {
16165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
16166  NonRepeatingHeadCode);
16167  int ForwardCount = 0;
16168  int ReverseCount = 0;
16169  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
16170  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
16171  // Forward corresponds to Main, Reverse to Other
16172  TTrainDataEntry *MainTrainDataPtr = 0;
16173  TTrainDataEntry *OtherTrainDataPtr = 0;
16174 
16175  // forward check
16176  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16177  {
16178  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16179  if(TDEntry.HeadCode == MainHeadCode)
16180  {
16181  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16182  {
16183  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16184  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
16185  {
16186  MainTrainDataPtr = &TrainDataVector.at(x);
16187  ForwardEntryPtr = &AVEntry;
16188  ForwardCount++;
16189  ForwardTDVectorNumber = x;
16190  }
16191  }
16192  }
16193  }
16194  if(ForwardCount == 0)
16195  // this is an exception because the headcodes are selected in the same order as the forward check
16196  {
16197  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
16198  }
16199  if(ForwardCount > 1)
16200  {
16201  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
16202  MainHeadCode);
16203  TrainDataVector.clear();
16204  Utilities->CallLogPop(1061);
16205  return(false);
16206  }
16207  // reverse check
16208  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16209  {
16210  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16211  if(TDEntry.HeadCode == NonRepeatingHeadCode)
16212  {
16213  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16214  {
16215  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16216  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
16217  {
16218  OtherTrainDataPtr = &TrainDataVector.at(x);
16219  ReverseCount++;
16220  ReverseEntryPtr = &AVEntry;
16221  ReverseTDVectorNumber = x;
16222  }
16223  }
16224  }
16225  }
16226 
16227  if(ReverseCount == 0)
16228  {
16229  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
16230  TrainDataVector.clear();
16231  Utilities->CallLogPop(1062);
16232  return(false);
16233  }
16234  if(ReverseCount > 1)
16235  {
16236  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
16237  NonRepeatingHeadCode);
16238  TrainDataVector.clear();
16239  Utilities->CallLogPop(1063);
16240  return(false);
16241  }
16242  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
16243  {
16244  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
16245  TrainDataVector.clear();
16246  Utilities->CallLogPop(1064);
16247  return(false);
16248  }
16249  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
16250  {
16251  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
16252  TrainDataVector.clear();
16253  Utilities->CallLogPop(1065);
16254  return(false);
16255  }
16256  if(SetDataAndCheckLocations)
16257  {
16258  if(ForwardEntryPtr->LocationName == "")
16259  {
16260  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16261  ". One or other service does not have a location set");
16262  TrainDataVector.clear();
16263  Utilities->CallLogPop(1066);
16264  return(false);
16265  }
16266  if(ReverseEntryPtr->LocationName == "")
16267  {
16268  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16269  ". One or other service does not have a location set");
16270  TrainDataVector.clear();
16271  Utilities->CallLogPop(1067);
16272  return(false);
16273  }
16274  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
16275  {
16276  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
16277  " is at a different location to the referencing train " + MainHeadCode);
16278  TrainDataVector.clear();
16279  Utilities->CallLogPop(1068);
16280  return(false);
16281  }
16282  }
16283  if(ForwardEntryPtr->Command == "F-nshs")
16284  // i.e. the non repeating link into the shuttle service
16285  {
16286  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
16287  {
16288  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
16289  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
16290  TrainDataVector.clear();
16291  Utilities->CallLogPop(1069);
16292  return(false);
16293  }
16294  }
16295  if(ForwardEntryPtr->Command == "Fns-sh")
16296  // i.e. the non repeating link out from the shuttle service
16297  {
16298  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
16299  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
16300  {
16301  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
16302  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
16303  TrainDataVector.clear();
16304  Utilities->CallLogPop(1070);
16305  return(false);
16306  }
16307  }
16308  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
16309  // i.e. a non repeating link to or from the shuttle service
16310  {
16311  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
16312  {
16313  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
16314  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
16315  TrainDataVector.clear();
16316  Utilities->CallLogPop(1071);
16317  return(false);
16318  }
16319  }
16320 /* it's allowed to have a different description
16321  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
16322  {
16323  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
16324  {
16325  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
16326  TrainDataVector.clear();
16327  Utilities->CallLogPop(1072);
16328  return false;
16329  }
16330  }
16331 */
16332  if(ForwardEntryPtr->Command == "Sns-sh")
16333  {
16334  if(ReverseEntryPtr->Command != "F-nshs")
16335  {
16336  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
16337  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
16338  TrainDataVector.clear();
16339  Utilities->CallLogPop(1073);
16340  return(false);
16341  }
16342  }
16343  if(ForwardEntryPtr->Command == "F-nshs")
16344  {
16345  if(ReverseEntryPtr->Command != "Sns-sh")
16346  {
16347  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
16348  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
16349  TrainDataVector.clear();
16350  Utilities->CallLogPop(1074);
16351  return(false);
16352  }
16353  else
16354  {
16355  if(SetDataAndCheckLocations)
16356  {
16357  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
16358  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16359  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16360  {
16361  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16362  }
16363  }
16364  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16365  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16366  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16367  }
16368  }
16369  if(ForwardEntryPtr->Command == "Sns-fsh")
16370  {
16371  if(ReverseEntryPtr->Command != "Fns-sh")
16372  {
16373  SecondPassMessage(GiveMessages,
16374  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
16375  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
16376  TrainDataVector.clear();
16377  Utilities->CallLogPop(1075);
16378  return(false);
16379  }
16380  }
16381  if(ForwardEntryPtr->Command == "Fns-sh")
16382  {
16383  if(ReverseEntryPtr->Command != "Sns-fsh")
16384  {
16385  SecondPassMessage(GiveMessages,
16386  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
16387  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
16388  TrainDataVector.clear();
16389  Utilities->CallLogPop(1076);
16390  return(false);
16391  }
16392  else
16393  {
16394  if(SetDataAndCheckLocations)
16395  {
16396  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
16397  // links to the non-repeating non-shuttle linked service
16398  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16399  // needed for creating formatted timetable
16400  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16401  {
16402  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16403  }
16404  }
16405  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16406  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16407  }
16408  }
16409  Utilities->CallLogPop(1077);
16410  return(true);
16411 }
16412 
16413 // ---------------------------------------------------------------------------
16414 
16415 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
16416 // Forward train is the finish shuttle entry 'Fns-sh'.
16417 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
16418 {
16419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
16420  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
16421  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16422  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16423  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
16424 
16425  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
16426  {
16427  Utilities->CallLogPop(1369);
16428  return(false);
16429  }
16430  else
16431  {
16432  Utilities->CallLogPop(1370);
16433  return(true);
16434  }
16435 }
16436 
16437 // ---------------------------------------------------------------------------
16438 
16439 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
16440 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
16441 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
16442 // don't ever need to and as designed would skip repeats.
16443 
16444 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
16445 {
16446  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
16447  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
16448  {
16449  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
16450  }
16451  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
16452  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
16453  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16454 
16455  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16456  {
16457  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16458  TrainDataVector.clear();
16459  Utilities->CallLogPop(1091);
16460  return(false);
16461  }
16462  while(LastActionCommand == "Fns")
16463  {
16464  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
16465  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16466  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16467  {
16468  SecondPassMessage(GiveMessages,
16469  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
16470  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16471  TrainDataVector.clear();
16472  Utilities->CallLogPop(1092);
16473  return(false);
16474  }
16475  }
16476  // exit the 'while' with LastActionCommand FSH-XX
16477  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
16478  {
16479  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
16480  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
16481  ". The linking of two or more shuttles is not permitted.");
16482  TrainDataVector.clear();
16483  Utilities->CallLogPop(1093);
16484  return(false);
16485  }
16486  Utilities->CallLogPop(1094);
16487  return(true);
16488 }
16489 
16490 // ---------------------------------------------------------------------------
16491 
16492 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
16493 {
16494  if(!GiveMessages)
16495  {
16496  return;
16497  }
16498  // if(ServiceReference == "") ShowMessage(Message);
16499  if(!CheckHeadCodeValidity(12, false, ServiceReference))
16500  {
16501  ShowMessage(ServiceReference + "(not a valid service ref.): " + Message); //amended at v2.15.1 to give information on 'service' so can find it in lh list
16502  }
16503  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
16504  // false means don't give messages within the function
16505  else
16506  {
16507  ShowMessage("Service " + ServiceReference + ": " + Message);
16508  }
16509 }
16510 
16511 // ---------------------------------------------------------------------------
16512 
16513 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
16514 {
16515  if(!GiveMessages)
16516  {
16517  return;
16518  }
16519  ShowMessage(Message);
16520 }
16521 
16522 // ---------------------------------------------------------------------------
16523 
16524 AnsiString TTrainController::MinsToAnsiTime(int Input) //added at v2.15.0
16525 {
16526  TrainController->LogEvent("MinsToAnsiTime");
16527  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MinsToAnsiTime," + Input);
16528  int Mins = Input, Hrs = 0;
16529  while(Mins > 59)
16530  {
16531  Mins -= 60;
16532  Hrs++;
16533  }
16534  AnsiString AnsiMins = AnsiString(Mins);
16535  if(AnsiMins.Length() == 1)
16536  {
16537  AnsiMins = "0" + AnsiMins;
16538  }
16539  AnsiString AnsiHrs = AnsiString(Hrs);
16540  if(AnsiHrs.Length() == 1)
16541  {
16542  AnsiHrs = "0" + AnsiHrs;
16543  }
16544  Utilities->CallLogPop(2577);
16545  return(AnsiHrs + ':' + AnsiMins);
16546 }
16547 
16548 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
16549 // ---------------------------------------------------------------------------
16550 
16551 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
16552 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
16553 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
16554 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
16555 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
16556 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
16557 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
16558 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
16559 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
16560 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
16561 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
16562 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
16563 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
16564 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
16565 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
16566 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
16567 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
16568 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
16569 // FailMissedDSC: 06:00:10: ERROR: 2F43 failed to change its description at Essex Road
16570 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
16571 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
16572 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
16573 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
16574 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
16575 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
16576 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
16577 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
16578 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
16579 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
16580 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
16581 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
16582 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
16583 {
16584  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
16585  AnsiString(ActionEventType) + "," + LocationID);
16586  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
16587 
16588  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
16589  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
16590 
16591  Prefix = " ERROR: ";
16592  if(ActionEventType == FailTrainEntry)
16593  {
16594  Prefix = " HELD: ";
16595  ErrorLog = " can't enter railway, train obstructing entry position ";
16596  WarningStr = " can't enter railway, train obstructing entry position ";
16597  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
16598  }
16599  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
16600  {
16601  Prefix = " HELD: ";
16602  ErrorLog = " can't enter railway, route set against it at entry position ";
16603  WarningStr = " can't enter railway, route set against it at entry position ";
16604  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
16605  }
16606  else if(ActionEventType == FailCreateTrain)
16607  {
16608  Prefix = " HELD: ";
16609  ErrorLog = " can't be created, train obstructing ";
16610  WarningStr = " can't be created, train obstructing ";
16611  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
16612  }
16613  else if(ActionEventType == FailCreateLockedRoute)
16614  {
16615  Prefix = " HELD: ";
16616  ErrorLog = " can't be created on a locked route at ";
16617  WarningStr = " can't be created on a locked route at ";
16618  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
16619  }
16620  else if(ActionEventType == FailEnterLockedRoute)
16621  {
16622  Prefix = " HELD: ";
16623  ErrorLog = " can't enter on a locked route at ";
16624  WarningStr = " can't enter on a locked route at ";
16625  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
16626  }
16627  else if(ActionEventType == FailCreatePoints)
16628  {
16629  Prefix = " HELD: ";
16630  ErrorLog = " can't be created, diverging points at ";
16631  WarningStr = " can't be created, diverging points at ";
16632  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
16633  }
16634  else if(ActionEventType == FailUnexpectedExitRailway)
16635  {
16636  ErrorLog = " left railway unexpectedly at ";
16637  UnexpectedExits++;
16638  }
16639  else if(ActionEventType == FailIncorrectExit)
16640  {
16641  ErrorLog = " left railway at an incorrect exit at ";
16642  IncorrectExits++;
16643  }
16644  else if(ActionEventType == FailLocTooShort)
16645  {
16646  ErrorLog = " failed to split - location too short at ";
16647  WarningStr = " failed to split, location too short at ";
16648  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
16649  }
16650  else if(ActionEventType == FailSplitDueToOtherTrain)
16651  {
16652  Prefix = " HELD: ";
16653  ErrorLog = " unable to split - other train obstructing at ";
16654  WarningStr = " unable to split - other train obstructing at ";
16655  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
16656  }
16657  else if(ActionEventType == FailUnexpectedBuffers)
16658  {
16659  ErrorLog = " stopped at buffers unexpectedly at position ";
16660  }
16661  else if(ActionEventType == FailMissedArrival)
16662  {
16663  ErrorLog = " failed to stop at ";
16664  MissedStops++;
16665  }
16666  else if(ActionEventType == FailMissedSplit)
16667  {
16668  ErrorLog = " failed to split at ";
16670  }
16671  else if(ActionEventType == FailMissedJBO)
16672  {
16673  ErrorLog = " failed to be joined by other train at ";
16675  }
16676  else if(ActionEventType == FailMissedDSC) //new at v2.15.0
16677  {
16678  ErrorLog = " failed to change its description at ";
16679 // OtherMissedEvents++; shouldn't count
16680  }
16681  else if(ActionEventType == FailMissedJoinOther)
16682  {
16683  ErrorLog = " failed to join other train at ";
16685  }
16686  else if(ActionEventType == FailMissedTerminate)
16687  {
16688  ErrorLog = " failed to terminate at ";
16690  }
16691  else if(ActionEventType == FailMissedNewService)
16692  {
16693  ErrorLog = " failed to form new service at ";
16695  }
16696  else if(ActionEventType == FailMissedExitRailway)
16697  {
16698  ErrorLog = " failed to exit railway ";
16700  }
16701  else if(ActionEventType == FailMissedChangeDirection)
16702  {
16703  ErrorLog = " failed to change direction at ";
16704 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
16705  }
16706  else if(ActionEventType == FailMissedPass)
16707  {
16708  ErrorLog = " failed to pass ";
16709 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
16710  }
16711  else if(ActionEventType == FailBuffersPreventingStart)
16712  {
16713  ErrorLog = " facing buffers and unable to start at ";
16714  }
16715  else if(ActionEventType == FailDerailed)
16716  {
16717  ErrorLog = " DERAILED at position ";
16718  Prefix = " DERAILMENT: ";
16719  Derailments++;
16720  }
16721  else if(ActionEventType == FailBufferCrash)
16722  {
16723  ErrorLog = " CRASHED INTO BUFFERS at ";
16724  Prefix = " CRASH: ";
16725  CrashedTrains++;
16726  }
16727  else if(ActionEventType == FailLevelCrossingCrash)
16728  {
16729  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
16730  Prefix = " CRASH: ";
16731  CrashedTrains++;
16732  }
16733  else if(ActionEventType == FailCrashed)
16734  {
16735  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
16736  Prefix = " CRASH: ";
16737  CrashedTrains++;
16738  CrashedTrains++;
16739  }
16740  else if(ActionEventType == FailSPAD)
16741  {
16742  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
16743  Prefix = " SPAD: ";
16744  SPADEvents++;
16745  }
16746  else if(ActionEventType == FailLockedRoute)
16747  {
16748  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
16749  Prefix = " SPAD RISK: ";
16750  SPADRisks++;
16751  }
16752  else if(ActionEventType == RouteForceCancelled)
16753  {
16754  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
16755  }
16756  else if(ActionEventType == WaitingForJBO)
16757  {
16758  Prefix = " WARNING: ";
16759  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
16760  WarningStr = " waiting to join " + OtherHeadCode + " at ";
16761  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
16762  }
16763  else if(ActionEventType == WaitingForFJO)
16764  {
16765  Prefix = " WARNING: ";
16766  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
16767  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
16768  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
16769  }
16770 
16771  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
16772  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
16773  Utilities->CallLogPop(1371);
16774 }
16775 
16776 // ---------------------------------------------------------------------------
16777 
16779 {
16780 /* //for testing purposes
16781  TrainDataEntry
16782  AnsiString HeadCode, Description;//null on creation
16783  int StartSpeed, MaxRunningSpeed;//both kph
16784  int RepeatNumber;
16785  TActionVector ActionVector;
16786  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
16787  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
16788 
16789  ActionVectorEntry
16790  TTimetableEntryType FormatType;
16791  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
16792  AnsiString LocationName, Command, OtherHeadCode;//null on creation
16793  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
16794  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16795  int RepeatNumber;
16796 
16797  TrainOperatingData
16798  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
16799  int TrainID;
16800  TRunningEntry RunningEntry;
16801  TDateTime StartTime;
16802  AnsiString HeadCode;
16803 */
16804  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
16805  std::ofstream OutFile("TrainData.csv");
16806 
16807  if(OutFile == 0)
16808  {
16809  ShowMessage("Output file TrainData.csv failed to open");
16810  Utilities->CallLogPop(1372);
16811  return;
16812  }
16813  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16814  {
16815  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16816  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
16817 
16818  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.FixedDescription.c_str() /* name changed at v2.16.1*/
16819  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
16820 
16821  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
16822  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
16823  "RepeatNumber" << '\n' << '\n';
16824  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16825  {
16826  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16827  AnsiString TimetableEntryTypeStr;
16828  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
16829  switch(AVEntry.FormatType)
16830  {
16831  case 0:
16832  {
16833  TimetableEntryTypeStr = "NoFormat";
16834  break;
16835  }
16836 
16837  case 1:
16838  {
16839  TimetableEntryTypeStr = "TimeLoc";
16840  break;
16841  }
16842 
16843  case 2:
16844  {
16845  TimetableEntryTypeStr = "TimeTimeLoc";
16846  break;
16847  }
16848 
16849  case 3:
16850  {
16851  TimetableEntryTypeStr = "TimeCmd";
16852  break;
16853  }
16854 
16855  case 4:
16856  {
16857  TimetableEntryTypeStr = "StartNew";
16858  break;
16859  }
16860 
16861  case 5:
16862  {
16863  TimetableEntryTypeStr = "TimeCmdHeadCode";
16864  break;
16865  }
16866 
16867  case 6:
16868  {
16869  TimetableEntryTypeStr = "FinRemHere";
16870  break;
16871  }
16872 
16873  case 7:
16874  {
16875  TimetableEntryTypeStr = "FNSShuttle";
16876  break;
16877  }
16878 
16879  case 8:
16880  {
16881  TimetableEntryTypeStr = "SNTShuttle";
16882  break;
16883  }
16884 
16885  case 9:
16886  {
16887  TimetableEntryTypeStr = "SNSShuttle";
16888  break;
16889  }
16890 
16891  case 10:
16892  {
16893  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
16894  break;
16895  }
16896 
16897  case 11:
16898  {
16899  TimetableEntryTypeStr = "FSHNewService";
16900  break;
16901  }
16902 
16903  case 12:
16904  {
16905  TimetableEntryTypeStr = "Repeat";
16906  break;
16907  }
16908 
16909  default:
16910  {
16911  TimetableEntryTypeStr = "Default";
16912  break;
16913  }
16914  }
16915  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
16916  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
16917  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
16918  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
16919  AVEntry.NumberOfRepeats << '\n';
16920  }
16921  OutFile << '\n';
16922  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
16923  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
16924  {
16925  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
16926  AnsiString RunningEntryStr;
16927  // NotStarted, Running, Exited
16928  switch(TOD.RunningEntry)
16929  {
16930  case 0:
16931  {
16932  RunningEntryStr = "NotStarted";
16933  break;
16934  }
16935 
16936  case 1:
16937  {
16938  RunningEntryStr = "Running";
16939  break;
16940  }
16941 
16942  case 2:
16943  {
16944  RunningEntryStr = "Exited";
16945  break;
16946  }
16947  }
16948  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
16949  }
16950  OutFile << '\n';
16951  }
16952  OutFile.close();
16953  Utilities->CallLogPop(1373);
16954 }
16955 
16956 // ---------------------------------------------------------------------------
16957 
16958 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
16959 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
16960 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed (in ClockTimer2 when StopTTClockFlag is false)
16961 {
16962  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
16963  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
16965  ShowMessage(Message);
16966  BaseTime = TDateTime::CurrentDateTime();
16967  StopTTClockFlag = false;
16968  Utilities->CallLogPop(1374);
16969 }
16970 
16971 // ---------------------------------------------------------------------------
16972 
16973 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
16974 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
16975 // from the start of the relevant vectors. Can't save the pointer values
16976 // as these will be different each time the vectors are created
16977 {
16978  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
16979  Utilities->SaveFileInt(SessionFile, TrainVector.size());
16980  for(unsigned int x = 0; x < TrainVector.size(); x++)
16981  {
16982  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
16983  }
16984  Utilities->CallLogPop(1375);
16985 }
16986 
16987 // ---------------------------------------------------------------------------
16988 
16989 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
16990 {
16991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
16992  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
16993  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16994  // by zero error in calculating AValue, use 1
16995  for(int x = 0; x < NumberOfTrains; x++)
16996  {
16997  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16998  // by zero error in calculating AValue, use 1
16999  NewTrain->LoadOneSessionTrain(0, SessionFile);
17000  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
17001  // added at v2.4.0. have to include as that value not stored in session file
17002  {
17003  NewTrain->StoppedWithoutPower = true;
17004  }
17005  TrainVector.push_back(*NewTrain);
17006  LastTrainLoaded = x;
17007  }
17008  delete NewTrain;
17009  Utilities->CallLogPop(1376);
17010 }
17011 
17012 // ---------------------------------------------------------------------------
17013 
17014 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
17015 {
17016  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
17017  int NumberOfTrains;
17018 
17019  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
17020  {
17021  Utilities->CallLogPop(1377);
17022  return(false);
17023  }
17024  for(int x = 0; x < NumberOfTrains; x++)
17025  {
17026  if(!(TTrain::CheckOneSessionTrain(InFile)))
17027  {
17028  Utilities->CallLogPop(1378);
17029  return(false);
17030  }
17031  }
17032  Utilities->CallLogPop(1379);
17033  return(true);
17034 }
17035 
17036 // ---------------------------------------------------------------------------
17037 
17038 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
17039 {
17040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
17041  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
17042  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
17043  {
17044  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
17045  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RearTrackVectorPosition);
17046  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
17047  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
17048  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
17049  }
17050  Utilities->CallLogPop(1380);
17051 }
17052 
17053 // ---------------------------------------------------------------------------
17054 
17055 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
17056 {
17057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
17058  TAllRoutes::TLockedRouteClass LockedRouteObject;
17059  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
17060 
17061  for(int x = 0; x < LockedRouteVectorSize; x++)
17062  {
17063  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
17064  LockedRouteObject.RearTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
17065  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
17066  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
17067  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
17068  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
17069  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
17070  }
17071  Utilities->CallLogPop(1381);
17072 }
17073 
17074 // ---------------------------------------------------------------------------
17075 
17076 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
17077 {
17078  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
17079  int LockedRouteVectorSize;
17080 
17081  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
17082  {
17083  Utilities->CallLogPop(1382);
17084  return(false);
17085  }
17086  for(int x = 0; x < LockedRouteVectorSize; x++)
17087  {
17088  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17089  {
17090  Utilities->CallLogPop(1383);
17091  return(false);
17092  }
17093  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17094  {
17095  Utilities->CallLogPop(1384);
17096  return(false);
17097  }
17098  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17099  {
17100  Utilities->CallLogPop(1385);
17101  return(false);
17102  }
17103  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
17104  {
17105  Utilities->CallLogPop(1386);
17106  return(false);
17107  }
17108  if(!Utilities->CheckFileDouble(SessionFile))
17109  {
17110  Utilities->CallLogPop(1387);
17111  return(false);
17112  }
17113  }
17114  Utilities->CallLogPop(1388);
17115  return(true);
17116 }
17117 
17118 // ---------------------------------------------------------------------------
17119 
17120 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
17121 {
17122  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
17123  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
17124  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
17125  {
17126  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
17127  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
17128  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
17129  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
17130  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
17131  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
17132  }
17133  Utilities->CallLogPop(1389);
17134 }
17135 
17136 // ---------------------------------------------------------------------------
17137 
17138 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17139 {
17140  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
17141  TContinuationAutoSigEntry ContinuationAutoSigObject;
17142  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
17143 
17144  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17145  {
17146  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
17147  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
17148  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
17149  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
17150  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
17151  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
17152  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
17153  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
17154  }
17155  Utilities->CallLogPop(1390);
17156 }
17157 
17158 // ---------------------------------------------------------------------------
17159 
17160 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17161 {
17162  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
17163  int ContinuationAutoSigVectorSize;
17164 
17165  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
17166  {
17167  Utilities->CallLogPop(1391);
17168  return(false);
17169  }
17170  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17171  {
17172  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17173  {
17174  Utilities->CallLogPop(1392);
17175  return(false);
17176  }
17177  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
17178  {
17179  Utilities->CallLogPop(1393);
17180  return(false);
17181  }
17182  if(!Utilities->CheckFileDouble(SessionFile))
17183  {
17184  Utilities->CallLogPop(1405);
17185  return(false);
17186  }
17187  if(!Utilities->CheckFileDouble(SessionFile))
17188  {
17189  Utilities->CallLogPop(1406);
17190  return(false);
17191  }
17192  if(!Utilities->CheckFileDouble(SessionFile))
17193  {
17194  Utilities->CallLogPop(1407);
17195  return(false);
17196  }
17197  if(!Utilities->CheckFileDouble(SessionFile))
17198  {
17199  Utilities->CallLogPop(1394);
17200  return(false);
17201  }
17202  }
17203  Utilities->CallLogPop(1395);
17204  return(true);
17205 }
17206 
17207 // ---------------------------------------------------------------------------
17208 
17209 /*
17210  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
17211  {
17212  public:
17213  AnsiString Description; ///< service description
17214  AnsiString HeadCode; ///< service headcode
17215  int RepeatNumber; ///< service RepeatNumber
17216  int IncrementalMinutes; ///< Repeat separation in minutes
17217  int IncrementalDigits; ///< Repeat headcode separation
17218  int VectorPosition; ///< TrackVectorPosition for the continuation element
17219  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17220  };
17221 
17222 
17223  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17224  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
17225 */
17226 
17228 // build this into timetable load so session loading can use it too
17229 // being a multimap it automatically sorts in ascending EventTime order
17230 {
17231  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
17233  // need to clear as this called twice when load a session
17234  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17235  {
17236  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17237  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
17238  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17239 
17240  if(AVFirstEntry.Command == "Snt")
17241  // new train (no need to include Snt-sh since they can't start at a continuation)
17242  {
17245  {
17247  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
17248  // retains this value for all repeats
17249  CTEEntry.RepeatNumber = 0; // for first entry
17250  CTEEntry.TrainDataEntryPtr = &TDEntry;
17251  // retains this value for all repeats
17252  CTEEntry.HeadCode = TDEntry.HeadCode;
17253  CTEEntry.FixedDescription = TDEntry.FixedDescription; //name changed at v2.16.1
17254  CTEEntry.IncrementalMinutes = 0;
17255  CTEEntry.IncrementalDigits = 0;
17256  if(AVLastEntry.FormatType == Repeat)
17257  {
17258  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
17259  // retains this value or 0 for all repeats
17260  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
17261  // retains this value or 0 for all repeats
17262  }
17263  CTEMMP.first = AVFirstEntry.EventTime;
17264  CTEMMP.second = CTEEntry;
17265  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17266  // base entry
17267  if(TDEntry.NumberOfTrains > 1)
17268  {
17269  if(AVLastEntry.FormatType != Repeat)
17270  {
17271  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
17272  }
17273  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
17274  {
17275  CTEEntry.RepeatNumber = y;
17276  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
17277  // CTEEntry.VectorPosition stays same
17278  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
17279  CTEMMP.second = CTEEntry;
17280  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17281  }
17282  }
17283  }
17284  }
17285  }
17286  Utilities->CallLogPop(1396);
17287 }
17288 
17289 // ---------------------------------------------------------------------------
17290 
17292 {
17293  // called when WarningFlashCount == 0 or when press zoomout button
17294  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
17295  if(!Display->ZoomOutFlag)
17296  {
17297  Utilities->CallLogPop(1156);
17298  return;
17299  }
17300  for(unsigned int x = 0; x < TrainVector.size(); x++)
17301  {
17302  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
17303  // if OldPlotElement[x] == -1 then ignore (not plotted)
17305  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
17306  }
17307  Display->Update();
17308  // need to keep this since Update() not called for PlotSmallOutput as too slow
17309  Utilities->CallLogPop(742);
17310 }
17311 
17312 // ---------------------------------------------------------------------------
17313 
17314 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
17315 {
17316  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
17317  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
17318  {
17319  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
17320  }
17321  Utilities->CallLogPop(740);
17322  return(TrainVector.at(VecPos));
17323 }
17324 
17325 // ---------------------------------------------------------------------------
17326 
17327 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
17328 {
17329  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
17330  AnsiString RetStr = "", PartStr = "";
17331 
17332 
17333 /*
17334  Have description & mass etc for train at top - header, then array of actions
17335 
17336  class TActionVectorEntry
17337  {
17338  public:
17339  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
17341  bool SignallerControl;
17343  bool Warning;
17345  int NumberOfRepeats;
17347  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
17349  TDateTime EventTime, ArrivalTime, DepartureTime;
17351  TNumList ExitList;
17353  TTimetableFormatType FormatType;
17355  TTimetableLocationType LocationType;
17357  TTimetableSequenceType SequenceType;
17359  TTimetableShuttleLinkType ShuttleLinkType;
17361  TTrainDataEntry *LinkedTrainEntryPtr;
17363  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
17365 
17366  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
17367 
17368  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
17369 
17370  class TTrainOperatingData
17371  {
17372  public:
17373  int TrainID;
17374  TActionEventType EventReported;
17375  TRunningEntry RunningEntry;
17376 
17377  //inline function
17378  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
17379  };
17380 
17381  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
17382 
17383  class TTrainDataEntry
17384  {
17385  public:
17386  AnsiString HeadCode, ServiceReference, Description;
17388  double MaxBrakeRate;
17390  double MaxRunningSpeed;
17392  double PowerAtRail;
17394  int Mass;
17396  int NumberOfTrains;
17398  int SignallerSpeed;
17400  int StartSpeed;
17402  TActionVector ActionVector;
17404  TTrainOperatingDataVector TrainOperatingDataVector;
17406 
17407  //inline function
17408  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
17409  };
17410 
17411  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
17412 
17413  //formatted timetable types
17414  class TOneTrainFormattedEntry
17415  {
17416  AnsiString Action;//includes location if relevanr
17417  AnsiString Time;
17418  };
17419 
17420  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17421 
17422  class TOneCompleteFormattedTrain//headcode + list of actions
17423  {
17424  public:
17425  AnsiString HeadCode;
17426  TOneFormattedTrainVector OneFormattedTrainVector;
17427  };
17428 
17429  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17430 
17431  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17432  {
17433  public:
17434  AnsiString Header;//description, mass, power, brake rate etc
17435  int NumberOfTrains;// number of repeats + 1
17436  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17437  };
17438 
17439 
17440  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17441  //end of formatted timetable types
17442 
17443 */
17444 
17445  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17446 
17447  // format "16/06/2009 20:55:17"
17448  // avoid characters in filename:= / \ : * ? " < > |
17449  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17450 
17451  AnsiString ShortTTName = "";
17452 
17453  for(int x = TTFileName.Length(); x > 0; x--)
17454  {
17455  if(TTFileName[x] == '\\')
17456  {
17457  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
17458  break;
17459  }
17460  }
17461 
17462  ShowMessage("Creates two timetables named " + ShortTTName +
17463  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
17464 
17465  Screen->Cursor = TCursor(-11); // Hourglass
17466 
17467  AnsiString FormatNoDPStr = "#######0";
17468  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
17469 
17471  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
17472  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
17473 
17474  // all timetable in formatted form
17475  //create the AllTTTrains vector
17476  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17477  {
17478  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
17479  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
17480  if(TrainDataEntry.Mass > 0)
17481  {
17482  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
17483  }
17484  if(TrainDataEntry.PowerAtRail > 0)
17485  {
17486  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
17487  }
17488  if(TrainDataEntry.MaxBrakeRate > 0)
17489  {
17490  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
17491  }
17492  if(TrainDataEntry.MaxRunningSpeed > 0)
17493  {
17494  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
17495  }
17496  FirstHeadCode = TrainDataEntry.HeadCode;
17497  int IncDigits = 0, IncMinutes = 0;
17498  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17499  if(!ActionVector.empty())
17500  {
17501  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17502  {
17503  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
17504  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17505  }
17506  }
17507  TTrainFormattedInformation OneTTLine;
17508  // contains all information for a single TT entry (including repeats)
17509  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
17510  {
17511  OneTTLine.Header = "";
17512  if((TrainDataEntry.FixedDescription != "") && (MassStr != "")) //name changed at v2.16.1
17513  {
17514  OneTTLine.Header = TrainDataEntry.FixedDescription + MassStr + PowerStr + BrakeStr + MaxSpeedStr; //name changed at v2.16.1
17515  }
17516  else if(TrainDataEntry.FixedDescription != "") //name changed at v2.16.1
17517  {
17518  OneTTLine.Header = TrainDataEntry.FixedDescription; //name changed at v2.16.1
17519  }
17520  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
17521  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
17522  for(unsigned int z = 0; z < ActionVector.size(); z++)
17523  {
17524  TOneTrainFormattedEntry OneTTEntry;
17525  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
17526  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
17527  AnsiString PartStr = "", TimeStr = "";
17528 /*
17529  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
17530  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
17531  ExitRailway};
17532  enum TTimetableSequenceType {NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry};
17533  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
17534  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
17535 */
17536  if(ActionVectorEntry.SequenceType == StartSequence)
17537  {
17538  if(ActionVectorEntry.FormatType == StartNew)
17539  {
17540  if(ActionVectorEntry.LocationName != "")
17541  {
17542  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17543  {
17544  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17545  }
17546  else
17547  {
17548  PartStr = "Created at " + ActionVectorEntry.LocationName;
17549  }
17550  }
17551  else // may be a named continuation or other element, and if so report that
17552  {
17553  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
17554  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17555  {
17556  if(LocName != "")
17557  {
17558  PartStr = "Enters at " + LocName;
17559  }
17560  else // use rear position if it's a continuation
17561  {
17562  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17563  }
17564  }
17565  else // not a continuation
17566  {
17567  if(LocName != "")
17568  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
17569  // but include anyway
17570  {
17571  PartStr = "Created at " + LocName;
17572  }
17573  else // use rear position again
17574  {
17575  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17576  }
17577  }
17578  }
17579  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
17580  }
17581  else if(ActionVectorEntry.FormatType == SNTShuttle)
17582  {
17583  if(y == 0) // first train
17584  {
17585  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17586  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
17587  }
17588  else
17589  {
17590  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17591  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17592  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
17593  } // y-1 for headcode above since it is the last repeat value that the train is from
17594 
17595  }
17596  else if(ActionVectorEntry.Command == "Sfs")
17597  {
17598  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
17599  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17600  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
17601  }
17602  else if(ActionVectorEntry.Command == "Sns")
17603  {
17604  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17605  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17606  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
17607  }
17608  else if(ActionVectorEntry.FormatType == SNSShuttle)
17609  {
17610  if(y == 0) // first entry from shuttle
17611  {
17612  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17613  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
17614  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
17615  }
17616  else
17617  {
17618  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17619  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17620  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
17621  } // y-1 for headcode above since it is the last repeat value that the train is from
17622 
17623  }
17624  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
17625  {
17626  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17627  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
17628  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
17629  AnsiString FirstHeadCode = TDE->HeadCode;
17630  int LastRepeatNumber = TDE->NumberOfTrains - 1;
17631  // a shuttle has to have at least 1 repeat
17632  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
17633  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
17634  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
17635  }
17636  }
17637  else if(ActionVectorEntry.SequenceType == IntermediateSequence)
17638  {
17639  if(ActionVectorEntry.FormatType == TimeTimeLoc)
17640  {
17641  // here need 2 entries if times different so push the first right away & the second later
17642  // if times same just give the arrival entry
17643  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
17644  {
17645  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17646  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17647  OneTTEntry.Action = PartStr;
17648  OneTTEntry.Time = TimeStr;
17649  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17650  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17651  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
17652  }
17653  else
17654  {
17655  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
17656  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17657  }
17658  }
17659  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
17660  {
17661  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17662  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17663  }
17664  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
17665  {
17666  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17667  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
17668  }
17669  else if(ActionVectorEntry.FormatType == PassTime)
17670  {
17671  PartStr = "Passes " + ActionVectorEntry.LocationName;
17672  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
17673  }
17674  else if(ActionVectorEntry.Command == "jbo")
17675  {
17676  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
17677  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17678  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
17679  }
17680  else if(ActionVectorEntry.Command == "fsp")
17681  {
17682  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17683  {
17684  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17685  }
17686  else
17687  {
17688  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17689  }
17690  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17691  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
17692  }
17693  else if(ActionVectorEntry.Command == "rsp")
17694  {
17695  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17696  {
17697  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17698  }
17699  else
17700  {
17701  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17702  }
17703  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17704  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
17705  }
17706  else if(ActionVectorEntry.Command == "cdt")
17707  {
17708  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
17709  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
17710  }
17711  else if(ActionVectorEntry.Command == "dsc")
17712  {
17713  PartStr = "Changes description at " + ActionVectorEntry.LocationName;
17714  TimeStr = Utilities->Format96HHMM(GetRepeatTime(76, ActionVectorEntry.EventTime, y, IncMinutes));
17715  }
17716  }
17717  else if(ActionVectorEntry.SequenceType == FinishSequence)
17718  {
17719  if(ActionVectorEntry.Command == "Fns")
17720  {
17721  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17722  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17723  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
17724  }
17725  else if(ActionVectorEntry.Command == "F-nshs")
17726  {
17727  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17728  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17729  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
17730  }
17731  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17732  {
17733  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
17734  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17735  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
17736  // y+1 because it's the NEXT service repeat number that is relevant
17737  }
17738  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17739  {
17740  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17741  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17742  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
17743  }
17744  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17745  {
17746  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17747  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17748  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
17749  // y+1 because it's the NEXT service repeat number that is relevant
17750  }
17751  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17752  {
17753  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
17754  // only used in chronological tt
17755  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
17756  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
17757  }
17758  else if(ActionVectorEntry.Command == "Frh")
17759  {
17760  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
17761  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
17762  if(z > 0)
17763  // should be for finish entry but include check for safety
17764  {
17765  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17766  {
17767  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17768  }
17769  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17770  {
17771  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17772  }
17773  else
17774  {
17775  TimeStr = " "; // shouldn't ever get here
17776  }
17777  }
17778  }
17779  else if(ActionVectorEntry.Command == "Fer")
17780  {
17781  AnsiString AllowedExits;
17782  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
17783  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
17784  }
17785  else if(ActionVectorEntry.Command == "Fjo")
17786  {
17787  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
17788  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17789  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
17790  }
17791  }
17792  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
17793  {
17794  continue; // no entry needed for a repeat
17795  }
17796  OneTTEntry.Action = PartStr;
17797  OneTTEntry.Time = TimeStr;
17798  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17799  // one per action
17800  }
17801  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
17802  // one per repeat
17803  }
17804  AllTTTrains->push_back(OneTTLine); // one per repeating train
17805  }
17806  // AllTTTrains vector now complete
17807 
17808  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
17809 
17810  if(TTFile == 0)
17811  {
17812  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
17813  delete AllTTTrains;
17814  Utilities->CallLogPop(1567);
17815  return;
17816  }
17817 /* formatted timetable types
17818  class TOneTrainFormattedEntry
17819  {
17820  AnsiString Action;//includes location if relevant
17821  AnsiString Time;
17822  };
17823 
17824  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17825 
17826  class TOneCompleteFormattedTrain//headcode + list of actions
17827  {
17828  public:
17829  AnsiString HeadCode;
17830  TOneFormattedTrainVector OneFormattedTrainVector;
17831  };
17832 
17833  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17834 
17835  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17836  {
17837  public:
17838  AnsiString Header;//description, mass, power, brake rate etc
17839  int NumberOfTrains;// number of repeats + 1
17840  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17841  };
17842 
17843  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17844  //end of formatted timetable types
17845 */
17846 
17847  // new layout using multiple rows
17848  TTFile << TableTitle.c_str() << '\n' << '\n';
17849  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17850  {
17851  TTFile << AllTTTrains->at(x).Header.c_str();
17852  TTFile << '\n';
17853  TTFile << ','; // for the blank line above the action list
17854  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17855  {
17856  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17857  {
17858  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
17859  }
17860  else
17861  {
17862  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
17863  }
17864  }
17865  TTFile << '\n' << '\n';
17866 
17867  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
17868  {
17869  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
17870  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17871  {
17872  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17873  {
17874  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
17875  }
17876  else
17877  {
17878  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
17879  }
17880  }
17881  TTFile << '\n';
17882  }
17883  TTFile << '\n' << '\n';
17884  }
17885 
17886  TTFile.close();
17887 
17888  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17889 
17890  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17891 
17892  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
17893 
17894  if(TTFile2 == 0)
17895  {
17896  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
17897  delete AllTTTrains;
17898  Utilities->CallLogPop(1710);
17899  return;
17900  }
17901  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
17902  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
17903  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
17904 
17905  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
17906  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
17907 
17908  // multimap of AnsiStrings with TimeString as key (to sort automatically)
17909 
17910  TTFile2 << TableTitle.c_str() << '\n' << '\n';
17911  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17912  {
17913  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17914  {
17915  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
17916  {
17917  bool GiveMessagesFalse = false;
17918  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
17919  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
17920  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
17921  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
17922  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
17923  {
17924  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
17925  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
17926  TimeString = TimeString.SubString(9, 5);
17927  ActionString += " " + OtherHeadCode;
17928  }
17929  if(TimeString.SubString(1, 7) == "End at ")
17930  // for Frh-sh final entry
17931  {
17932  TimeString = TimeString.SubString(8, 5);
17933  }
17934  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
17935  AnsiMultiMapEntry.first = TimeString;
17936  AnsiMultiMapEntry.second = OneLine;
17937  TAMM->insert(AnsiMultiMapEntry);
17938  }
17939  }
17940  }
17941 
17942  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
17943  {
17944  TTFile2 << (AMMIT->second).c_str();
17945  }
17946  delete AllTTTrains;
17947  delete TAMM;
17948  TTFile2.close();
17949  Utilities->CallLogPop(1580);
17950 }
17951 
17952 // ---------------------------------------------------------------------------
17953 
17954 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
17955  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
17956 {
17957 
17958  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
17959  bool AnalysisError = false;
17960  AnsiString SequenceLog = "SequenceLog\n";
17961 
17962 /* Double crosslink (shuttle) table:
17963 
17964 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
17965  Code ShuttleLink- EntryPtr ShuttleLink-
17966  HeadCode EntryPtr
17967 
17968 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
17969 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
17970 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
17971 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
17972 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
17973 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
17974 
17975 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
17976 */
17977 
17978  try
17979  {
17980  //New section at v2.5.0 for tt conflict analysis
17981  /*
17982  typedef std::list<AnsiString> TServiceCallingLocsList;
17983  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
17984 
17986  struct TLocServiceTimes
17987  {
17988  AnsiString Location;
17989  AnsiString ServiceAndRepeatNum;
17990  AnsiString AtLocTime;
17991  AnsiString ArrTime;
17992  AnsiString DepTime;
17993  AnsiString FrhMarker;
17994  };
17995  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17996  */
17997 
17998  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
17999  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
18000 
18001 //create TrainDataVectorCopy and populate service refs with /1, /2 etc
18002  TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
18003  TTrainDataVector::iterator TDVIt, TDVCopyIt;
18004  int Suffix = 0;
18005  int IteratorNumber = 0;
18006  AnsiString AnsiSuffix = "";
18007  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
18008  {
18009  IteratorNumber++; //first value in loop is 1
18010  Suffix = 0;
18011  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
18012  {
18013  if(TDVCopyIt->HeadCode == TDVIt->HeadCode)
18014  {
18015  Suffix++; //first value is 1
18016  AnsiSuffix = AnsiString(Suffix);
18017  TDVCopyIt->ServiceReference = TDVIt->HeadCode + "/" + AnsiSuffix; //set both the HeadCode + any forward slashes and numbers, this is because sometimes
18018  TDVCopyIt->HeadCode = TDVIt->HeadCode + "/" + AnsiSuffix; //service refs are used and sometimes H/Cs, so need them to be the same,
18019  } //they are all unique at this point anyway
18020  }
18021  }
18022 //now make all linked pointers in ActionVectorEntries point to links in the vector copy (still point to original vector at this stage)
18023 //and set the linked headcodes to the correct values - all unique at this point
18024  int Increment = 0, SlashPos;
18025  TActionVectorIterator AVEIt;
18026  AnsiString LinkedHeadCode;
18027 
18028  for(TDVCopyIt = TrainDataVectorCopy.begin(); TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
18029  {
18030  for(AVEIt = TDVCopyIt->ActionVector.begin(); AVEIt != TDVCopyIt->ActionVector.end(); AVEIt++)
18031  {
18032  if(AVEIt->LinkedTrainEntryPtr != NULL)
18033  {
18034  Increment = AVEIt->LinkedTrainEntryPtr - &TrainDataVector.at(0);
18035  AVEIt->LinkedTrainEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
18036  //now set AVEIt->OtherHeadCode to the linked headcodes with /1, /2 etc
18037  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
18038  LinkedHeadCode = (*AVEIt->LinkedTrainEntryPtr).ServiceReference;
18039  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
18040  SlashPos = 0;
18041  for(int x = LinkedHeadCode.Length(); x > 0; x--)
18042  {
18043  if(LinkedHeadCode[x] == '/')
18044  {
18045  SlashPos = LinkedHeadCode.Length() - x + 1;
18046  break;
18047  }
18048  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
18049  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
18050  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
18051  {
18052  break;
18053  }
18054  }
18055  //now strip off any prefix
18056  AVEIt->OtherHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
18057  }
18058  else
18059  {
18060  AVEIt->OtherHeadCode = "";
18061  }
18062  if(AVEIt->NonRepeatingShuttleLinkEntryPtr != NULL)
18063  {
18064  Increment = AVEIt->NonRepeatingShuttleLinkEntryPtr - &TrainDataVector.at(0);
18065  AVEIt->NonRepeatingShuttleLinkEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
18066  //now set AVEIt->NonRepeatingShuttleLinkHeadCode to the linked headcodes with /1, /2 etc
18067  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
18068  LinkedHeadCode = (*AVEIt->NonRepeatingShuttleLinkEntryPtr).ServiceReference;
18069  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
18070  SlashPos = 0;
18071  for(int x = LinkedHeadCode.Length(); x > 0; x--)
18072  {
18073  if(LinkedHeadCode[x] == '/')
18074  {
18075  SlashPos = LinkedHeadCode.Length() - x + 1;
18076  break;
18077  }
18078  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
18079  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
18080  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
18081  {
18082  break;
18083  }
18084  }
18085  //now strip off any prefix
18086  AVEIt->NonRepeatingShuttleLinkHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
18087  }
18088  else
18089  {
18090  AVEIt->NonRepeatingShuttleLinkHeadCode = "";
18091  }
18092  }
18093  }
18094  //from here only TrainDataVectorCopy used
18095  SequenceLog += "1\n";
18096  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
18097  TServiceCallingLocsList ServiceCallingLocsList;
18098  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
18099  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18100  {
18101  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18102  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18103  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
18104  ServiceCallingLocsList.clear();
18105  if(ActionVector.empty())
18106  {
18107  continue;
18108  }
18109  if(ActionVector.at(0).SignallerControl)
18110  {
18111  continue;
18112  }
18113  for(unsigned int z = 0; z < ActionVector.size(); z++)
18114  {
18115  TActionVectorEntry AVE = ActionVector.at(z);
18116  if(AVE.FormatType == StartNew)
18117  {
18118  if(AVE.LocationType == AtLocation) //located Snt
18119  {
18120  ServiceCallingLocsList.push_back(AVE.LocationName);
18121  }
18122  else //unlocated Snt (could be entering at continuation)
18123  {
18125  if(TE.ActiveTrackElementName != "")
18126  {
18127  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
18128  }
18129  else
18130  {
18131  int HLoc = TE.HLoc;
18132  int VLoc = TE.VLoc;
18133  AnsiString HString;
18134  AnsiString VString;
18135  if(HLoc < 0)
18136  {
18137  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18138  }
18139  else
18140  {
18141  HString = AnsiString(HLoc);
18142  }
18143  if(VLoc < 0)
18144  {
18145  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18146  }
18147  else
18148  {
18149  VString = AnsiString(VLoc);
18150  }
18151  ServiceCallingLocsList.push_back(HString + '-' + VString);
18152  }
18153  }
18154  }
18155  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18156  {
18157  ServiceCallingLocsList.push_back(AVE.LocationName);
18158  }
18159  else if(AVE.FormatType == TimeLoc) //z must be > 0
18160  {
18161  if(ServiceCallingLocsList.back() != AVE.LocationName)
18162  {
18163  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
18164  }
18165  }
18166  else if(AVE.FormatType == PassTime)
18167  {
18168  ServiceCallingLocsList.push_back(AVE.LocationName);
18169  }
18170  else if(AVE.FormatType == TimeTimeLoc)
18171  {
18172  ServiceCallingLocsList.push_back(AVE.LocationName);
18173  }
18174  else if(AVE.Command == "cdt") //list if not next to start or finish
18175  {
18176  if(ActionVector.at(z-1).SequenceType == StartSequence)
18177  {
18178  continue;
18179  }
18180  else if(ActionVector.at(z+1).SequenceType == FinishSequence) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
18181  {
18182  continue;
18183  }
18184  else
18185  {
18186  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
18187  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
18188  }
18189  }
18190  else if(AVE.FormatType == ExitRailway) //Fer
18191  {
18192  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
18193  AnsiString LName = TE.ActiveTrackElementName;
18194  if(LName != "")
18195  {
18196  ServiceCallingLocsList.push_back(LName);
18197  }
18198  else
18199  {
18200  int HLoc = TE.HLoc;
18201  int VLoc = TE.VLoc;
18202  AnsiString HString;
18203  AnsiString VString;
18204  if(HLoc < 0)
18205  {
18206  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18207  }
18208  else
18209  {
18210  HString = AnsiString(HLoc);
18211  }
18212  if(VLoc < 0)
18213  {
18214  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18215  }
18216  else
18217  {
18218  VString = AnsiString(VLoc);
18219  }
18220  ServiceCallingLocsList.push_back(HString + '-' + VString);
18221  }
18222  }
18223  }
18224  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
18225  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
18226  }
18227  //AllServiceCallingLocsMap built
18228  SequenceLog += "2\n";
18229 /*
18230 // this sequence is to test the validity of AllServiceCallingLocsMap
18231  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18232  std::ofstream Test(TestFile.c_str());
18233 
18234  if(TestFile == 0)
18235  {
18236  ShowMessage("TestFile failed to open - can't be created");
18237  Utilities->CallLogPop();
18238  return false;
18239  }
18240 
18241  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
18242  {
18243  Test << ASCLIt->first << '\n'; //service ref
18244  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
18245  {
18246  Test << *SCLIt << '\n';
18247  }
18248  Test << "\n\n";
18249  }
18250  Test.close();
18251  Utilities->CallLogPop();
18252  return true;
18253 */
18254  //initialise variables before calc LastTTTime & build LocServiceTimesVector
18255  if(TrainDataVectorCopy.empty())
18256  {
18257  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
18258  Utilities->CallLogPop(2209);
18259  return(false);
18260  }
18261  TLocServiceTimes TLSTEntry;
18262  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
18263  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
18264  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
18265  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
18266  LastTTTime = "";
18267  SequenceLog += "3\n";
18268  //calculate LastTTTime
18269  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18270  {
18271  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18272  TActionVector &ActionVector = TrainDataEntry.ActionVector;
18273  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
18274  TDateTime LastTDTime;
18275  int IncMinutes = 0;
18276  NumTrains = TrainDataEntry.NumberOfTrains;
18277  if(ActionVector.empty())
18278  {
18279  continue;
18280  }
18281  if(ActionVector.at(0).SignallerControl)
18282  {
18283  continue;
18284  }
18285  if(AVLast->FormatType == Repeat)
18286  {
18287  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18288  AVLast--; //now points to the command before the repeat
18289  }
18290  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
18291  {
18292  AVLast--; //points to last timed entry
18293  }
18294  //here AVLast points to last entry with a time
18295  if(AVLast->ArrivalTime != TDateTime(-1))
18296  {
18297  LastTDTime = AVLast->ArrivalTime;
18298  }
18299  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
18300  {
18301  LastTDTime = AVLast->EventTime;
18302  }
18303  else
18304  {
18305  continue; //shouldn't ever reach here but if do then skip this service
18306  }
18307  if(NumTrains == 1)
18308  {
18309  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
18310  }
18311  else
18312  {
18313  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
18314  }
18315  if(LastAnsiTime > LastTTTime)
18316  {
18317  LastTTTime = LastAnsiTime;
18318  }
18319  }
18320  SequenceLog += "4\n";
18321 //build LocServiceTimesVector
18322 
18323 /*
18324  struct TLocServiceTimes
18325  {
18326  AnsiString Location;
18327  AnsiString ServiceAndRepeatNum;
18328  AnsiString AtLocTime;
18329  AnsiString ArrTime;
18330  AnsiString DepTime;
18331  AnsiString FrhMarker;
18332  };
18333  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
18334 
18335 This works as follows:
18336 ServiceAndRepeatNum is taken from the TrainDataVectorCopy as it is the same for all actionvector entries
18337 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
18338 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
18339 
18340 Every action for every train is examined and times entered as follows:-
18341 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
18342 b) an unlocated Snt: entry time becomes DepTime
18343 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
18344 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
18345 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
18346 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
18347 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
18348 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
18349 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
18350 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
18351 */
18352  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18353  {
18354  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18355  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18356  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
18357  int IncMinutes = 0;
18358  NumTrains = TrainDataEntry.NumberOfTrains;
18359  if(ActionVector.empty())
18360  {
18361  continue;
18362  }
18363  if(ActionVector.at(0).SignallerControl)
18364  {
18365  continue;
18366  }
18367  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
18368  {
18369  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18370  }
18371  for(int y = 0; y < NumTrains; y++) //y is the repeat number
18372  {
18373  if(NumTrains == 1)
18374  {
18375  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
18376  }
18377  else if(y == 0)
18378  {
18379  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
18380  }
18381  else
18382  {
18383  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
18384  }
18385  for(unsigned int z = 0; z < ActionVector.size(); z++)
18386  {
18387  TActionVectorEntry AVE = ActionVector.at(z);
18388  TLSTEntry.AtLocTime = "";
18389  TLSTEntry.ArrTime = "";
18390  TLSTEntry.DepTime = "";
18391  TLSTEntry.Location = "";
18392  TLSTEntry.FrhMarker = "";
18393 
18394  if(AVE.FormatType == StartNew) //Snt only
18395  {
18396  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
18397  {
18398  TLSTEntry.Location = AVE.LocationName;
18399  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
18400  LocServiceTimesVector.push_back(TLSTEntry);
18401 
18402  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18403  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
18404  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18405  {
18406  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18407  {
18408  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
18409  break;
18410  }
18411  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18412  {
18413  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
18414  break;
18415  }
18416  }
18417  if(FoundStopTime == "")
18418  {
18419  throw Exception("Failure to determine FoundStopTime for located Snt");
18420  }
18421  int WhileCount = 0;
18422  while(true)
18423  {
18424  //add minutes until reach FoundStopTime but don't add that time
18425  WhileCount++;
18426  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18427  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18428  TLSTEntry.DepTime = "";
18429  TLSTEntry.ArrTime = "";
18430  if(IncTime >= FoundStopTime) //don't add that time
18431  {
18432  break;
18433  }
18434  LocServiceTimesVector.push_back(TLSTEntry);
18435  if(WhileCount > 2000)
18436  {
18437  throw Exception("While loop failed to break in 2000 loops for located Snt");
18438  }
18439  }
18440  }
18441  else //unlocated Snt, use the EventTime as DepTime for this vector
18442  {
18444  if(TE.ActiveTrackElementName != "")
18445  {
18446  TLSTEntry.Location = TE.ActiveTrackElementName;
18447  }
18448  else
18449  {
18450  int HLoc = TE.HLoc;
18451  int VLoc = TE.VLoc;
18452  AnsiString HString;
18453  AnsiString VString;
18454  if(HLoc < 0)
18455  {
18456  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18457  }
18458  else
18459  {
18460  HString = AnsiString(HLoc);
18461  }
18462  if(VLoc < 0)
18463  {
18464  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18465  }
18466  else
18467  {
18468  VString = AnsiString(VLoc);
18469  }
18470  TLSTEntry.Location = HString + '-' + VString;
18471  }
18472  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
18473  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18474  LocServiceTimesVector.push_back(TLSTEntry);
18475  }
18476  }
18477 
18478  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18479  {
18480  TLSTEntry.Location = AVE.LocationName;
18481  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
18482  LocServiceTimesVector.push_back(TLSTEntry);
18483  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18484  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18485  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18486  {
18487  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18488  {
18489  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
18490  break;
18491  }
18492  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18493  {
18494  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
18495  break;
18496  }
18497  }
18498  if(FoundStopTime == "")
18499  {
18500  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18501  }
18502  int WhileCount = 0;
18503  while(true)
18504  {
18505  //add minutes until reach FoundStopTime but don't add that time
18506  WhileCount++;
18507  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18508  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18509  TLSTEntry.DepTime = "";
18510  TLSTEntry.ArrTime = "";
18511  if(IncTime >= FoundStopTime) //don't add that time
18512  {
18513  break;
18514  }
18515  LocServiceTimesVector.push_back(TLSTEntry);
18516  if(WhileCount > 2000)
18517  {
18518  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18519  }
18520  }
18521  }
18522 
18523  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
18524  {
18525  TLSTEntry.Location = AVE.LocationName;
18526  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
18527  {
18528  bool SkipAddingMinutes = false;
18529  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
18530  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18531  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
18532  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18533  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18534  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18535  {
18536  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18537  {
18538  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
18539  break;
18540  }
18541  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered for in a later test
18542  {
18543  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
18544  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr != NULL) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr != NULL)))
18545  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
18546  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
18547  {
18548  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
18549  SkipAddingMinutes = true;
18550  }
18551  break;
18552  }
18553  }
18554  if(FoundStopTime == "")
18555  {
18556  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18557  }
18558  if(!SkipAddingMinutes)
18559  {
18560  int WhileCount = 0;
18561  while(true)
18562  {
18563  //add minutes until reach FoundStopTime but don't add that time
18564  WhileCount++;
18565  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18566  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18567  TLSTEntry.DepTime = "";
18568  TLSTEntry.ArrTime = "";
18569  if(IncTime >= FoundStopTime) //don't add that time
18570  {
18571  break;
18572  }
18573  LocServiceTimesVector.push_back(TLSTEntry);
18574  if(WhileCount > 2000)
18575  {
18576  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18577  }
18578  }
18579  }
18580  }
18581  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
18582  {
18583  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
18584  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18585  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
18586  {
18587  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
18588  {
18589  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
18590  LocServiceTimesVector.pop_back();
18591  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
18592  }
18593  else //just add the dep & atloc times
18594  {
18595  TLSTEntry.ArrTime = "";
18596  LocServiceTimesVector.push_back(TLSTEntry);
18597  }
18598  }
18599  else //just add the dep & atloc times
18600  {
18601  TLSTEntry.ArrTime = "";
18602  LocServiceTimesVector.push_back(TLSTEntry);
18603  }
18604  }
18605  }
18606 
18607  else if(AVE.FormatType == TimeTimeLoc)
18608  {
18609  TLSTEntry.Location = AVE.LocationName;
18610  if(AVE.ArrivalTime > TDateTime(-1)) //should be
18611  {
18612  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
18613  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18614  }
18615  if(AVE.DepartureTime > TDateTime(-1)) //should be
18616  {
18617  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
18618  }
18619  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
18620  {
18621  LocServiceTimesVector.push_back(TLSTEntry);
18622  }
18623  else
18624  {
18625  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
18626  TLSTEntry.DepTime = "";
18627  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
18628  TLSTEntry.ArrTime = ""; //done with this now
18629  while(TLSTEntry.AtLocTime < TempDepTime)
18630  {
18631  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18632  if(TLSTEntry.AtLocTime == TempDepTime)
18633  {
18634  TLSTEntry.DepTime = TempDepTime; //restore value
18635  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
18636  }
18637  else
18638  {
18639  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
18640  }
18641  }
18642  }
18643  }
18644 
18645  else if(AVE.FormatType == PassTime) //added at v2.9.1
18646  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
18647  TLSTEntry.Location = AVE.LocationName;;
18648  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
18649  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
18650  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
18651  TLSTEntry.ArrTime = ""; //need to reset this to null
18652  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
18653  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
18654  }
18655 
18656  else if(AVE.FormatType == ExitRailway) //Fer
18657  {
18658  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
18659  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
18660  //be wrong, but can't guess from here & most will have same name
18661  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
18662  if(LName != "")
18663  {
18664  TLSTEntry.Location = LName;
18665  }
18666  else
18667  {
18668  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
18669  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
18670  AnsiString HString;
18671  AnsiString VString;
18672  if(HLoc < 0)
18673  {
18674  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18675  }
18676  else
18677  {
18678  HString = AnsiString(HLoc);
18679  }
18680  if(VLoc < 0)
18681  {
18682  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18683  }
18684  else
18685  {
18686  VString = AnsiString(VLoc);
18687  }
18688  TLSTEntry.Location = HString + '-' + VString;
18689  }
18690  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
18691  }
18692 
18693  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
18694  {
18695  AnsiString FrhTime;
18696  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
18697  {
18698  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
18699  }
18700  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
18701  {
18702  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
18703  }
18704  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
18705  TLSTEntry.Location = AVE.LocationName;
18706  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18707  TLSTEntry.FrhMarker = "Frh";
18708  LocServiceTimesVector.push_back(TLSTEntry);
18709  TLSTEntry.FrhMarker = "";
18710  //add all times from next minute to end of timetable
18711  while(IncTime <= LastTTTime)
18712  {
18713  TLSTEntry.AtLocTime = IncTime;
18714  LocServiceTimesVector.push_back(TLSTEntry);
18715  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18716  }
18717  }
18718 
18719  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
18720  {
18721  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
18722  {
18723  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
18724  TLSTEntry.Location = AVE.LocationName;
18725  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18726  TLSTEntry.FrhMarker = "Frh";
18727  LocServiceTimesVector.push_back(TLSTEntry);
18728  TLSTEntry.FrhMarker = "";
18729  //add all times from next minute to end of timetable
18730  while(IncTime <= LastTTTime)
18731  {
18732  TLSTEntry.AtLocTime = IncTime;
18733  LocServiceTimesVector.push_back(TLSTEntry);
18734  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18735  }
18736  }
18737  }
18738 
18739  else if(AVE.SequenceType == FinishSequence) //other finish types - all located & all link to another service
18740  {
18741  //nothing is done here as the entry will be listed at this time under the new service reference
18742  }
18743  }
18744  }
18745  }
18746  SequenceLog += "5\n";
18747  //now sort in location order
18748  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
18749  //LocServiceTimesVector now complete & sorted in location order
18750 
18751 /*
18752 //start of debugging section
18753 //create LocServiceTimesVector output file for debugging purposes
18754  AnsiString LSTVTestFile = CurDir + "\\Formatted timetables\\LSTVTestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18755  std::ofstream LSTVFile(LSTVTestFile.c_str());
18756  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
18757  {
18758  LSTVFile << LSTVIt->Location + '\n';
18759  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
18760  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
18761  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
18762  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
18763  if(LSTVIt->FrhMarker == "")
18764  {
18765  LSTVFile << "Not Frh\n";
18766  }
18767  else
18768  {
18769  LSTVFile << LSTVIt->FrhMarker + '\n';
18770  }
18771  LSTVFile << '\n';
18772  }
18773  LSTVFile.close();
18774  Utilities->CallLogPop();
18775  return(true);
18776 //end of debugging section
18777 */
18778  //declare pointers for use in printouts
18779  TLocServiceTimesVector::iterator Ptr1, Ptr2;
18780 
18781  //set up the output file
18782  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18783  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
18784 
18785  std::ofstream TTFile3(TTFileName3.c_str());
18786 
18787  if(TTFile3 == 0)
18788  {
18789  ShowMessage("Conflict Analysis file failed to open - can't be created");
18790  Utilities->CallLogPop(2210);
18791  return(false);
18792  }
18793  if(LocServiceTimesVector.empty())
18794  {
18795  ShowMessage("No timetabled services found");
18796  TTFile3.close();
18797  DeleteFile(TTFileName3);
18798  Utilities->CallLogPop(2211);
18799  return(false);
18800  }
18801  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
18802  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
18803  SequenceLog += "6\n";
18804 
18805 /*
18806 //print out TrainDataVectorCopy & TrainDataVector for debugging purposes, TrainDataVectorCopy first
18807 
18808 // Double crosslink (shuttle) table:
18809 //Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
18810 // Code ShuttleLink- EntryPtr ShuttleLink-
18811 // HeadCode EntryPtr
18812 
18813 //Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
18814 //Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
18815 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18816 //Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
18817 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18818 //Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
18819 //
18820 //Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
18821 
18822 //NOTE: from above for F-nshs & Sns-fsh in the TrainDataVectorCopy the OtherLinkedHeadCode will be correct as it is derived from LinkTrainEntryPtr which is correct
18823 //but for the original TrainDataVector the OtherLinkedHeadCode will be incorrect, hence have to use the NonRepeatingShuttleLinkHeadCode as that is the correct one
18824 //these were errors when first coded but work ok, just keep in mind when making any changes
18825 
18826 std::ofstream TDVCFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVectorCopy.txt").c_str());
18827 AnsiString OHC = "", NRHC = "";
18828 AnsiString OLk = "", NRLk = "";
18829 TDVCFile << "Note that for the TrainDataVectorCopy OH and OLk should always be the same, as OH is derived from OHLk; and similarly for NR and NRLk\n\n";
18830 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
18831 {
18832  TDVCFile << TDVCIt->ServiceReference + '\n';
18833  TDVCFile << TDVCIt->Description + '\n';
18834  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
18835  {
18836  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
18837  if(AVE.OtherHeadCode == "")
18838  {
18839  OHC = "OH 0";
18840  }
18841  else
18842  {
18843  OHC = "OH " + AVE.OtherHeadCode;
18844  }
18845  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18846  {
18847  NRHC = "NR 0";
18848  }
18849  else
18850  {
18851  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18852  }
18853  if(TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18854  {
18855  OLk = "OLk 0";
18856  }
18857  else
18858  {
18859  OLk = "OLk " + TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18860  }
18861  if(TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18862  {
18863  NRLk = "NRLk 0";
18864  }
18865  else
18866  {
18867  NRLk = "NRLk " + TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18868  }
18869 
18870  if(AVE.FormatType == TimeCmd) //cdt only
18871  {
18872  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18873  }
18874  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18875  {
18876  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18877  }
18878  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18879  {
18880  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18881  }
18882  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18883  {
18884  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18885  }
18886  else if(AVE.FormatType == StartNew)
18887  {
18888  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-1, AVE.RearStartOrRepeatMins).ElementID
18889  << " FrontStartID " << Track->TrackElementAt(-2, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18890  }
18891  else if(AVE.FormatType == SNTShuttle)
18892  {
18893  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-3, AVE.RearStartOrRepeatMins).ElementID
18894  << " FrontStartID " << Track->TrackElementAt(-4, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18895  }
18896  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18897  {
18898  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18899  }
18900  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18901  {
18902  TDVCFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18903  }
18904  else if(AVE.FormatType == TimeTimeLoc)
18905  {
18906  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18907  }
18908  else if(AVE.FormatType == PassTime)
18909  {
18910  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18911  }
18912  else if(AVE.FormatType == ExitRailway)
18913  {
18914  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18915  }
18916  else if(AVE.FormatType == FinRemHere)
18917  {
18918  TDVCFile << "Frh" << '\n';
18919  }
18920  }
18921  TDVCFile << '\n';
18922 }
18923 TDVCFile.close();
18924 
18925 //print out original TrainDataVector for comparison
18926 std::ofstream TDVFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVector.txt").c_str());
18927 TDVFile << "Note that in the TrainDataVector, non-repeating shuttle link services F-nshs and Sns-fsh use the non-repeating headcode (NR) values for the corresponding "
18928  "shuttle headcodes when it should be the other headcode (OH), and the other headcode is unused. The link values are the right way round. Also OH & NR "
18929  "values ARE headcodes and not service references, but OLk and NRLk values are service references.\n\n";
18930 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18931 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18932 for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18933 {
18934  TDVFile << TDVIt->ServiceReference + '\n';
18935  TDVFile << TDVIt->Description + '\n';
18936  for(unsigned int x = 0; x < TDVIt->ActionVector.size(); x++)
18937  {
18938  TActionVectorEntry AVE = TDVIt->ActionVector.at(x);
18939  if(AVE.OtherHeadCode == "")
18940  {
18941  OHC = "OH 0";
18942  }
18943  else
18944  {
18945  OHC = "OH " + AVE.OtherHeadCode;
18946  }
18947  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18948  {
18949  NRHC = "NR 0";
18950  }
18951  else
18952  {
18953  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18954  }
18955  if(TDVIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18956  {
18957  OLk = "OLk 0";
18958  }
18959  else
18960  {
18961  OLk = "OLk " + TDVIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18962  }
18963  if(TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18964  {
18965  NRLk = "NRLk 0";
18966  }
18967  else
18968  {
18969  NRLk = "NRLk " + TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18970  }
18971 
18972  if(AVE.FormatType == TimeCmd) //cdt only
18973  {
18974  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18975  }
18976  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18977  {
18978  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18979  }
18980  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18981  {
18982  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18983  }
18984  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18985  {
18986  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18987  }
18988  else if(AVE.FormatType == StartNew)
18989  {
18990  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-5, AVE.RearStartOrRepeatMins).ElementID
18991  << " FrontStartID " << Track->TrackElementAt(-6, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18992  }
18993  else if(AVE.FormatType == SNTShuttle)
18994  {
18995  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-7, AVE.RearStartOrRepeatMins).ElementID
18996  << " FrontStartID " << Track->TrackElementAt(-8, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18997  }
18998  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18999  {
19000  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
19001  }
19002  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
19003  {
19004  TDVFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
19005  }
19006  else if(AVE.FormatType == TimeTimeLoc)
19007  {
19008  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
19009  }
19010  else if(AVE.FormatType == PassTime)
19011  {
19012  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
19013  }
19014  else if(AVE.FormatType == ExitRailway)
19015  {
19016  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
19017  }
19018  else if(AVE.FormatType == FinRemHere)
19019  {
19020  TDVFile << "Frh" << '\n';
19021  }
19022  }
19023  TDVFile << '\n';
19024 }
19025 TDVFile.close();
19026 //end of debugging
19027 */
19028  //arrivals
19029  if(ArrChecked)
19030  {
19031  //sort in ArrTime order for each location
19032  Ptr1 = LocServiceTimesVector.begin();
19033  Ptr2 = Ptr1 + 1;
19034  while(Ptr2 != LocServiceTimesVector.end())
19035  {
19036  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19037  {
19038  Ptr2++;
19039  if(Ptr2 == LocServiceTimesVector.end())
19040  {
19041  break;
19042  }
19043  }
19044  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
19045  Ptr1 = Ptr2; //first entry with next name
19046  if(Ptr2 != LocServiceTimesVector.end())
19047  {
19048  Ptr2++;
19049  }
19050  }
19051 
19052  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
19053 
19054  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
19055  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
19056  MinuteString = " minutes";
19057  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19058  if(ArrRange == 1)
19059  {
19060  MinuteString = " minute";
19061  }
19062  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
19063  TTFile3 << ",Platforms,Trains\n\n";
19064 
19065  Ptr1 = LocServiceTimesVector.begin();
19066  Ptr2 = Ptr1 + 1;
19067  while(Ptr2 != LocServiceTimesVector.end())
19068  {
19069  PreviousService = "";
19070  NumTrainsAtLoc = 0;
19071  ServiceAndRepeatNumTotal = "";
19072  NumPlats = 0;
19073  NumPlatsAtThisLocCalculated = false;
19074  BasicTime = "";
19075  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19076  {
19077  PreviousService = "";
19078  NumTrainsAtLoc = 0;
19079  ServiceAndRepeatNumTotal = "";
19080  NumPlats = 0;
19081  NumPlatsAtThisLocCalculated = false;
19082  BasicTime = "";
19083  Ptr1++;
19084  Ptr2++;
19085  if(Ptr2 == LocServiceTimesVector.end())
19086  {
19087  break;
19088  }
19089  }
19090  if(Ptr2 == LocServiceTimesVector.end())
19091  {
19092  break;
19093  }
19094  while(Ptr2->Location == Ptr1->Location)
19095  {
19096  PreviousService = "";
19097  NumTrainsAtLoc = 0;
19098  ServiceAndRepeatNumTotal = "";
19099  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
19100  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19101  {
19102  break;
19103  }
19104  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
19105  {
19106  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
19107  Ptr1++;
19108  Ptr2++;
19109  if(Ptr2 == LocServiceTimesVector.end())
19110  {
19111  break;
19112  }
19113  if(Ptr2->Location != Ptr1->Location)
19114  {
19115  break;
19116  }
19117  }
19118  if(Ptr2 == LocServiceTimesVector.end())
19119  {
19120  break;
19121  }
19122  if(Ptr2->Location != Ptr1->Location)
19123  {
19124  break;
19125  }
19126  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
19127  {
19128  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
19129  {
19130  break;
19131  }
19132  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19133  {
19134  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
19135  NumPlatsAtThisLocCalculated = true;
19136  }
19137  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19138  {
19139  if(ServiceAndRepeatNumTotal == "")
19140  {
19141  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19142  NumTrainsAtLoc = 1;
19143  }
19144  else
19145  {
19146  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19147  }
19148  }
19149  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19150  if(ServiceAndRepeatNumTotal == "")
19151  {
19152  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19153  NumTrainsAtLoc = 1;
19154  }
19155  else
19156  {
19157  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19158  }
19159  Ptr1 = Ptr2;
19160  Ptr2++;
19161  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
19162  {
19163  int MaxNumberOfSameDirections = 0;
19164  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19165  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19166  {
19167 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19168  TTFile3.close();
19169  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19170 // Utilities->CallLogPop(2224);
19171 // return false;
19172  }
19173  AnsiString Asterisk = "";
19174  if(MaxNumberOfSameDirections >= NumPlats)
19175  {
19176  Asterisk = "* ";
19177  }
19178  //print out a single line for number of trains at loc with all service refs
19179  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19180  ArrivalsPrinted = true;
19181  ServiceAndRepeatNumTotal = "";
19182  }
19183  if(Ptr2 == LocServiceTimesVector.end())
19184  {
19185  break;
19186  }
19187  if(Ptr2->Location != Ptr1->Location)
19188  {
19189  break;
19190  }
19191  }
19192  if(Ptr2 == LocServiceTimesVector.end())
19193  {
19194  break;
19195  }
19196  }
19197  }
19198  if(!ArrivalsPrinted)
19199  {
19200  TTFile3 << "Nothing to report for arrivals";
19201  }
19202  TTFile3 << "\n\n";
19203  }
19204  //end of routine for arrivals
19205  SequenceLog += "7\n";
19206  //departures
19207  if(DepChecked)
19208  {
19209  //sort in DepTime order for each location
19210  Ptr1 = LocServiceTimesVector.begin();
19211  Ptr2 = Ptr1 + 1;
19212  while(Ptr2 != LocServiceTimesVector.end())
19213  {
19214  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19215  {
19216  Ptr2++;
19217  if(Ptr2 == LocServiceTimesVector.end())
19218  {
19219  break;
19220  }
19221  }
19222  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
19223  Ptr1 = Ptr2; //first entry with next name
19224  if(Ptr2 != LocServiceTimesVector.end())
19225  {
19226  Ptr2++;
19227  }
19228  }
19229 
19230  //routine for departures - number of trains departing within the specified range with services listed at the end
19231  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
19232  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
19233  MinuteString = " minutes";
19234  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19235  if(DepRange == 1)
19236  {
19237  MinuteString = " minute";
19238  }
19239  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
19240  TTFile3 << ",Platforms,Trains\n\n";
19241 
19242  Ptr1 = LocServiceTimesVector.begin();
19243  Ptr2 = Ptr1 + 1;
19244  while(Ptr2 != LocServiceTimesVector.end())
19245  {
19246  PreviousService = "";
19247  NumTrainsAtLoc = 0;
19248  ServiceAndRepeatNumTotal = "";
19249  NumPlats = 0;
19250  NumPlatsAtThisLocCalculated = false;
19251  BasicTime = "";
19252  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19253  {
19254  PreviousService = "";
19255  NumTrainsAtLoc = 0;
19256  ServiceAndRepeatNumTotal = "";
19257  NumPlats = 0;
19258  NumPlatsAtThisLocCalculated = false;
19259  BasicTime = "";
19260  Ptr1++;
19261  Ptr2++;
19262  if(Ptr2 == LocServiceTimesVector.end())
19263  {
19264  break;
19265  }
19266  }
19267  if(Ptr2 == LocServiceTimesVector.end())
19268  {
19269  break;
19270  }
19271  while(Ptr2->Location == Ptr1->Location)
19272  {
19273  PreviousService = "";
19274  NumTrainsAtLoc = 0;
19275  ServiceAndRepeatNumTotal = "";
19276  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
19277  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19278  {
19279  break;
19280  }
19281  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
19282  {
19283  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
19284  Ptr1++;
19285  Ptr2++;
19286  if(Ptr2 == LocServiceTimesVector.end())
19287  {
19288  break;
19289  }
19290  if(Ptr2->Location != Ptr1->Location)
19291  {
19292  break;
19293  }
19294  }
19295  if(Ptr2 == LocServiceTimesVector.end())
19296  {
19297  break;
19298  }
19299  if(Ptr2->Location != Ptr1->Location)
19300  {
19301  break;
19302  }
19303  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
19304  {
19305  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
19306  {
19307  break;
19308  }
19309  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19310  {
19311  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
19312  NumPlatsAtThisLocCalculated = true;
19313  }
19314  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19315  {
19316  if(ServiceAndRepeatNumTotal == "")
19317  {
19318  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19319  NumTrainsAtLoc = 1;
19320  }
19321  else
19322  {
19323  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19324  }
19325  }
19326  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19327  if(ServiceAndRepeatNumTotal == "")
19328  {
19329  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19330  NumTrainsAtLoc = 1;
19331  }
19332  else
19333  {
19334  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19335  }
19336  Ptr1 = Ptr2;
19337  Ptr2++;
19338  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
19339  {
19340  int MaxNumberOfSameDirections = 0;
19341  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19342  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19343  {
19344 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19345  TTFile3.close();
19346  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19347 // Utilities->CallLogPop(2225);
19348 // return false;
19349  }
19350  AnsiString Asterisk = "";
19351  if(MaxNumberOfSameDirections >= NumPlats)
19352  {
19353  Asterisk = "* ";
19354  }
19355  //print out a single line for number of trains at loc with all service refs
19356  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19357  DeparturesPrinted = true;
19358  ServiceAndRepeatNumTotal = "";
19359  }
19360  if(Ptr2 == LocServiceTimesVector.end())
19361  {
19362  break;
19363  }
19364  if(Ptr2->Location != Ptr1->Location)
19365  {
19366  break;
19367  }
19368  }
19369  if(Ptr2 == LocServiceTimesVector.end())
19370  {
19371  break;
19372  }
19373  }
19374  }
19375  if(!DeparturesPrinted)
19376  {
19377  TTFile3 << "Nothing to report for departures";
19378  }
19379  TTFile3 << "\n\n";
19380  }
19381  //end of routine for departures
19382  SequenceLog += "8\n";
19383 
19384  //list trains at locations at same time
19385 
19386  if(AtLocChecked)
19387  {
19388  //sort in AtLocTime order for each location
19389  Ptr1 = LocServiceTimesVector.begin();
19390  Ptr2 = Ptr1 + 1;
19391  while(Ptr2 != LocServiceTimesVector.end())
19392  {
19393  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19394  {
19395  Ptr2++;
19396  if(Ptr2 == LocServiceTimesVector.end())
19397  {
19398  break;
19399  }
19400  }
19401  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
19402  Ptr1 = Ptr2; //first entry with next name
19403  if(Ptr2 != LocServiceTimesVector.end())
19404  {
19405  Ptr2++;
19406  }
19407  }
19408 
19409  //print out simultaneous AtLocs (don't need range of times for AtLocs)
19410  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
19411  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
19412  TTFile3 << ",Platforms,Trains,\n\n";
19413  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19414  Ptr1 = LocServiceTimesVector.begin();
19415  Ptr2 = Ptr1 + 1;
19416  while(Ptr2 != LocServiceTimesVector.end())
19417  {
19418  PreviousService = "";
19419  ServiceAndRepeatNumTotal = "";
19420  NumTrainsAtLoc = 0;
19421  NumPlats = 0;
19422  NumPlatsAtThisLocCalculated = false;
19423  FrhCount = 0;
19424 
19425  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19426  {
19427  PreviousService = "";
19428  ServiceAndRepeatNumTotal = "";
19429  NumTrainsAtLoc = 0;
19430  NumPlats = 0;
19431  NumPlatsAtThisLocCalculated = false;
19432  FrhCount = 0;
19433  Ptr1++;
19434  Ptr2++;
19435  if(Ptr2 == LocServiceTimesVector.end())
19436  {
19437  break;
19438  }
19439  }
19440  if(Ptr2 == LocServiceTimesVector.end())
19441  {
19442  break;
19443  }
19444  while(Ptr2->Location == Ptr1->Location)
19445  {
19446  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
19447  {
19448  FrhCount++;
19449  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19450  }
19451  PreviousService = "";
19452  NumTrainsAtLoc = 0;
19453  ServiceAndRepeatNumTotal = "";
19454  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19455  {
19456  break;
19457  }
19458  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
19459  {
19460  Ptr1++;
19461  if(Ptr1->FrhMarker == "Frh")
19462  {
19463  FrhCount++;
19464  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19465  }
19466  Ptr2++;
19467  if(Ptr2 == LocServiceTimesVector.end())
19468  {
19469  break;
19470  }
19471  if(Ptr2->Location != Ptr1->Location)
19472  {
19473  break;
19474  }
19475  }
19476  if(Ptr2 == LocServiceTimesVector.end())
19477  {
19478  break;
19479  }
19480  if(Ptr2->Location != Ptr1->Location)
19481  {
19482  break;
19483  }
19484  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
19485  {
19486  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
19487  {
19488  break;
19489  }
19490  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19491  {
19492  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
19493  NumPlatsAtThisLocCalculated = true;
19494  }
19495  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19496  {
19497  if(ServiceAndRepeatNumTotal == "")
19498  {
19499  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
19500  NumTrainsAtLoc = 1;
19501  }
19502  else
19503  {
19504  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
19505  }
19506  }
19507  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
19508  if(ServiceAndRepeatNumTotal == "")
19509  {
19510  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
19511  NumTrainsAtLoc = 1;
19512  }
19513  else
19514  {
19515  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
19516  }
19517  Ptr1 = Ptr2;
19518  if(Ptr1->FrhMarker == "Frh")
19519  {
19520  FrhCount++;
19521  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19522  }
19523  Ptr2++;
19524  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
19525  {
19526 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
19527 //new text //don't print out if all remainers or if only 1 train at loc
19528  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
19529 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
19530 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
19531  {
19532  AnsiString Asterisk = "";
19533  if(NumTrainsAtLoc > NumPlats)
19534  {
19535  Asterisk = "* ";
19536  }
19537  //print out a single line for number of trains at loc with all service refs
19538  if(FrhCount == 0)
19539  {
19540  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
19541  }
19542  else if(FrhCount == 1)
19543  {
19544  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
19545  }
19546  else
19547  {
19548  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
19549  }
19550  LastFrhCount = FrhCount;
19551  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
19552  AtLocsPrinted = true;
19553  ServiceAndRepeatNumTotal = "";
19554  }
19555  }
19556  if(Ptr2 == LocServiceTimesVector.end())
19557  {
19558  break;
19559  }
19560  if(Ptr2->Location != Ptr1->Location)
19561  {
19562  break;
19563  }
19564  }
19565  if(Ptr2 == LocServiceTimesVector.end())
19566  {
19567  break;
19568  }
19569  }
19570  }
19571  if(!AtLocsPrinted)
19572  {
19573  TTFile3 << "Nothing to report for trains at locations";
19574  }
19575  TTFile3 << "\n\n";
19576  //end of simultaneous AtLocs
19577  }
19578  SequenceLog += "9\n";
19579 
19580 /*
19581 //start of debugging section
19582  //print out the full vector here for testing purposes
19583  TTFile3 << "Full LocServiceTimesVector\n\n";
19584  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
19585 
19586  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
19587  {
19588  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
19589  }
19590 
19591  TTFile3 << "\n\n\n";
19592 //end of debugging
19593 */
19594 
19595 /*cdt analysis - added at v2.10.0
19596 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
19597 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
19598 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
19599 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
19600 service.
19601 
19602 Use The TrainDataVectorCopy as that has all unique service refs.
19603 
19604 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
19605 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
19606 
19607 First create a new TrainDataVector from earlier copy as above with single services
19608 */
19609  if(DirChecked)
19610  {
19611  //direction analysis added at v2.10.0
19612  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
19613  TTrainDataVector SingleServiceVector, PartServiceVector;
19614 
19615  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
19616  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
19617  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
19618  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
19619  {
19621  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19622  {
19623  TDE.ActionVector.erase(TDE.ActionVector.end() - 1); //strip repeat entry if present
19624  }
19625  const TActionVector &AV = TDE.ActionVector;
19626  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19627  {
19628  SingleServiceEntry = TDE;
19629  TActionVector &SSAV = SingleServiceEntry.ActionVector;
19630  for(unsigned int y = 0; y < SSAV.size(); y++)
19631  {
19632  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
19633  {
19634  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
19635  break; //finished with this one
19636  }
19637  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
19638  {
19639  PartServiceEntry = TDE; //start with complete entry
19640  PartServiceEntry.ActionVector.clear(); //clear AV
19641  for(unsigned int z = 0; z <= y; z++)
19642  {
19643  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
19644  if(z == y)
19645  {
19646  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
19647  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
19648  }
19649  }
19650  PartServiceVector.push_back(PartServiceEntry);
19651  if(SSAV.at(y).Command == "fsp")
19652  {
19653  SSAV.at(y).Command = "Front split - original service continues below";
19654  SSAV.at(y).OtherHeadCode = "";
19655  }
19656  if(SSAV.at(y).Command == "rsp")
19657  {
19658  SSAV.at(y).Command = "Rear split - original service continues below";
19659  SSAV.at(y).OtherHeadCode = "";
19660  }
19661  //don't break & continue here because the original train carries on
19662  }
19663  else if(SSAV.at(y).Command == "Fns")
19664  {
19665  SSAV.at(y).Command = "chr-Fns";
19666  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19667  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19668  break; //from y loop
19669  }
19670  else if(SSAV.at(y).Command == "Fns-sh")
19671  {
19672  SSAV.at(y).Command = "chr-Fns-sh";
19673  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19674  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19675  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19676  break; //from y loop
19677  }
19678  else if(SSAV.at(y).Command == "F-nshs")
19679  {
19680  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19681  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19682  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19683  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19684  break; //from y loop
19685  }
19686  }
19687  }
19688  }
19689  SequenceLog += "10\n";
19690  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
19691 
19692  //Now add Sns & Sns-sh services to PartServiceVector entries
19693  AnsiString NextRef;
19694  while(!PartServiceVector.empty())
19695  {
19696  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
19697  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
19698  {
19699  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
19700  {
19701  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
19702  }
19703  }
19704  //find it in TrainDataVectorCopy
19705  bool FinishType = true, FoundFlag = false;
19706  while(FinishType)
19707  {
19708  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
19709  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
19710  if(FoundFlag)
19711  {
19712  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
19713  {
19714  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19715  {
19716  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19717  }
19718  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19719  {
19720  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19721  }
19722  else
19723  {
19724  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
19725  {
19726  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19727  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
19728  PartServiceVector.erase(PartServiceVector.begin());
19729  break; //from y loop
19730  }
19731  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
19732  {
19733  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
19734  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
19735  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
19736  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
19737  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
19738  if(TempEntry.ActionVector.at(y).Command == "fsp")
19739  {
19740  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
19741  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19742  }
19743  if(TempEntry.ActionVector.at(y).Command == "rsp")
19744  {
19745  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
19746  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19747  }
19748  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19749  }
19750  else if(TempEntry.ActionVector.at(y).Command == "Fns")
19751  {
19752  TempEntry.ActionVector.at(y).Command = "chr-Fns";
19753  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19754  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
19755  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19756  break; //from y loop
19757  }
19758  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
19759  {
19760  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
19761  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19762  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19763  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19764  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19765  break; //from y loop
19766  }
19767  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
19768  {
19769  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19770  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19771  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19772  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19773  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19774  break; //from y loop
19775  }
19776  }
19777  }
19778  }
19779  else
19780  {
19781  SequenceLog += + "11\n";
19782  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
19783  }
19784  }
19785  }
19786  if(!PartServiceVector.empty())
19787  {
19788  SequenceLog += "12\n";
19789  throw Exception("PartServiceVector should be empty here - size = " + AnsiString(PartServiceVector.size()));
19790  }
19791  SequenceLog += "13\n";
19792  /*
19793  form:-
19794  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
19795  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
19796  then multiple entries, separated by commas, of the form:-
19797 
19798  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
19799  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
19800  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
19801 
19802  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
19803  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
19804  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
19805 
19806  HH:MM;Command (cdt) }TimeCmd }
19807  HH:MM;Command;new description (dsc) }TimeCmdDescription }
19808  HH:MM;Location (arr & dep) }TimeLoc }
19809  HH:MM;HH:MM;Location }TimeTimeLoc }
19810  HH:MM;pas;Location }PassTime }
19811  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
19812  HH:MM;Fer;set of allowable IDs }ExitRailway }
19813  Command (Frh only) }FinRemHere }
19814 
19815  R;mm;dd;nn. Repeat Repeat entry
19816 
19817  Formats:
19818 
19819  Command only: Frh
19820  Time;Command: cdt
19821  Time;Command;new description: dsc
19822  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
19823  Time;Command;2 Element IDs: Snt
19824  Time;Comand;n Element IDs: Fer
19825  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
19826  Time;Command;2 Element IDs;Headcode Snt-sh
19827  Time;Command;Location pas
19828  Time;Location Arr Dep
19829  Time;Time;Location Arr & dep together
19830  */
19831 
19832 /*
19833 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
19834 checking forwards until it comes to a continuation (no report), a location name that is not null and
19835 different to the train's front element name (whether null or not) (no report), a leading point
19836 (no report) or buffers (report).
19837 */
19838  bool BufferFacingUnReportedFlag = true;
19839  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19840  {
19841  TTrackElement ThisElement, NextElement;
19842  TTrainDataEntry TDE = SingleServiceVector.at(x);
19843  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19844  {
19845  SequenceLog += "13a\n";
19846  throw Exception("Repeat entry present in SingleServiceVector at position " + AnsiString(x));
19847  }
19848  const TActionVector &AV = TDE.ActionVector;
19849  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19850  {
19851  bool BufferFlag = false;
19852  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
19853  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
19854  AnsiString FrontLocName = AV.at(0).LocationName;
19855  int NextEntryPos, NextExitPos;
19856  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
19857  int ThisExitPos;
19858  if(ThisElement.Conn[0] == RearTVPos)
19859  {
19860  ThisExitPos = 1;
19861  }
19862  else if(ThisElement.Conn[1] == RearTVPos)
19863  {
19864  ThisExitPos = 0;
19865  }
19866  else if(ThisElement.Conn[2] == RearTVPos)
19867  {
19868  ThisExitPos = 3;
19869  }
19870  else if(ThisElement.Conn[3] == RearTVPos)
19871  {
19872  ThisExitPos = 2;
19873  }
19874  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
19875  {
19876  BufferFlag = true;
19877  }
19878  else //continue tracking forwards
19879  {
19880  while(true)
19881  {
19882  if(ThisElement.Conn[ThisExitPos] == -1)
19883  {
19884  SequenceLog = "13b\n";
19885  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
19886  }
19887  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
19888  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
19889  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
19890  {
19891  BufferFlag = false; //should already be false
19892  break;
19893  }
19894  else if(NextElement.TrackType == Continuation)
19895  {
19896  BufferFlag = false;
19897  break;
19898  }
19899  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
19900  {
19901  BufferFlag = false;
19902  break;
19903  }
19904  else if(NextElement.TrackType == Buffers)
19905  {
19906  BufferFlag = true;
19907  break;
19908  }
19909  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
19910  {
19911  ThisElement = NextElement;
19912  ThisExitPos = 0;
19913  continue;
19914  }
19915  else
19916  {
19917  if(NextEntryPos == 0)
19918  {
19919  NextExitPos = 1;
19920  }
19921  else if(NextEntryPos == 1)
19922  {
19923  NextExitPos = 0;
19924  }
19925  else if(NextEntryPos == 2)
19926  {
19927  NextExitPos = 3;
19928  }
19929  else if(NextEntryPos == 3)
19930  {
19931  NextExitPos = 2;
19932  }
19933  }
19934  ThisElement = NextElement;
19935  ThisExitPos = NextExitPos;
19936  }
19937  }
19938  if(BufferFlag)
19939  {
19940  if(BufferFacingUnReportedFlag)
19941  {
19942  TTFile3 << "Train facing direction on creation analysis:-\n\n";
19943  BufferFacingUnReportedFlag = false;
19944  }
19945  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
19946  }
19947  }
19948  }
19949  if(BufferFacingUnReportedFlag)
19950  {
19951  TTFile3 << "Nothing to report for train facing directions\n\n";
19952  }
19953  else
19954  {
19955  TTFile3 << '\n';
19956  }
19957  SequenceLog += "13c\n";
19958 
19959  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
19960  AnsiString LocationNameToBeChecked = "";
19961  bool MissingcdtUnreportedFlag = true;
19962  TNumList MarkerList;
19963  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19964  {
19965  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19966  unsigned int y = 0;
19967  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures won't be marked if not changed
19968  bool FullBreak = false;
19969  MarkerList.clear();
19970  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
19971  // first discard unlocated Snt entries as they don't have location name set
19972  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
19973  {
19974  y = 1;
19975  }
19976  while((y < TDEntry.ActionVector.size()) && !FullBreak)
19977  // need to check each location name separately in turn, skipped for SignallerControl entries
19978  {
19979  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
19980  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
19981  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
19982  {
19983  break; // out of the 'while' loop since have reached the end
19984  }
19985  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
19986  FirstInstance = y;
19987  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
19988  {
19989  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19990  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
19991  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19992  (AVEntry.Command == "Frh-sh"))
19993  {
19994  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
19995  }
19996  if(AVEntry.Command == "cdt")
19997  {
19998  break; // out of the 'z' loop since the check is only valid up to a change of direction
19999  }
20000  if(AVEntry.LocationName == LocationNameToBeChecked)
20001  {
20002  continue; // keep going while name same
20003  }
20004  if(AVEntry.LocationName != LocationNameToBeChecked)
20005  // if name different check forwards to see if repeats
20006  {
20007  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
20008  {
20009  if(TDEntry.ActionVector.at(a).Command == "cdt")
20010  {
20011  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
20012  }
20013  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
20014  {
20015  SecondInstance = a;
20016  AnsiString Sequence = TDEntry.ServiceReference;
20017  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
20018  {
20019  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
20020  {
20021  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
20022  }
20023  }
20024  if(MissingcdtUnreportedFlag)
20025  {
20026  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
20027  }
20028  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
20029  MarkerList.push_back(FirstInstance);
20030  MarkerList.push_back(SecondInstance);
20031  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
20032  MissingcdtUnreportedFlag = false;
20033  FullBreak = true; //no more checks for this sequence
20034  break; //out of the a & z loops
20035  }
20036  }
20037  break; // out of the 'z' loop since have checked 'a' as far as need to
20038  }
20039  }
20040  y++;
20041  }
20042  }
20043  if(MissingcdtUnreportedFlag)
20044  {
20045  TTFile3 << "Nothing to report for missing changes of direction\n\n";
20046  }
20047  else
20048  {
20049  TTFile3 << '\n';
20050  }
20051  SequenceLog += "14\n";
20052 
20053 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
20054  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
20055  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
20056  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
20057  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
20058  included in both then ok, else report as questionable. If one list is empty then it is reported.
20059 */
20060  typedef std::list<AnsiString> TLocList;
20061  TLocList BackwardList, ForwardList;
20062  bool IntroLineNeeded = true;
20063  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
20064  {
20065  unsigned int cdtPosition = 9999;
20066  AnsiString cdtLocation = "";
20067  bool FoundSameName = false;
20068  MarkerList.clear();
20069  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
20070  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
20071  // need to check each location name separately in turn, skipped for SignallerControl entries
20072  {
20073  BackwardList.clear();
20074  ForwardList.clear();
20075  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
20076  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
20077  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
20078  (AVEntry.Command == "Frh-sh"))
20079  {
20080  if(MarkerList.empty())
20081  {
20082  break; // out of the 'y' loop since have reached the end & nothing to report
20083  }
20084  else
20085  {
20086  AnsiString Sequence = TDEntry.ServiceReference;
20087  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
20088  {
20089  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
20090  {
20091  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
20092  }
20093  }
20094  MarkerList.sort();
20095  if(IntroLineNeeded)
20096  {
20097  TTFile3 << "Questionable change of direction analysis.\n\n";
20098  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
20099  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
20100  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
20101  TTFile3 << "make sure that none has been included incorrectly:\n\n";
20102  IntroLineNeeded = false;
20103  }
20104  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
20105  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
20106  break;
20107  }
20108  }
20109  if(AVEntry.Command != "cdt")
20110  {
20111  continue; //only looking for cdts
20112  }
20113  //here have found a cdt
20114  cdtPosition = y;
20115  cdtLocation = AVEntry.LocationName;
20116  for(int z = y - 1; z >= 0; z--)
20117  {
20118  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
20119  if(AVEntry2.Command == "cdt")
20120  {
20121  break; //don't look further back than the last cdt
20122  }
20123  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
20124  {
20125  BackwardList.push_back(AVEntry2.LocationName);
20126  }
20127  }
20128  BackwardList.sort();
20129  BackwardList.unique();
20130  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
20131  {
20132  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
20133  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
20134  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
20135  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
20136  {
20137  break; // out of the 'z' loop since have reached another cdt or the end
20138  }
20139  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
20140  {
20141  ForwardList.push_back(AVEntry3.LocationName);
20142  }
20143  }
20144  ForwardList.sort();
20145  ForwardList.unique();
20146  FoundSameName = false;
20147  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
20148  if(!BackwardList.empty() && !ForwardList.empty())
20149  {
20150  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
20151  {
20152  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
20153  {
20154  if(*BLIt == *FLIt)
20155  {
20156  FoundSameName = true;
20157  }
20158  }
20159  }
20160  }
20161  if(!FoundSameName) //report the inability to find same name
20162  {
20163  MarkerList.push_back(cdtPosition);
20164  }
20165  }
20166  }
20167  if(IntroLineNeeded)
20168  {
20169  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
20170  }
20171  else
20172  {
20173  TTFile3 << '\n';
20174  }
20175 /*
20176 //debug section
20177 //print all SSVector for diagnostic purposes
20178  TTFile3 << "Whole SSVector\n\n";
20179  TNumList EmptyList;
20180  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
20181  {
20182  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
20183  }
20184 //end of debug section
20185 */
20186  }
20187  SequenceLog += "15\n";
20188  TTFile3.close();
20189  Utilities->CallLogPop(2212);
20190  return(true);
20191  }
20192 
20193  catch(const Exception &e) //non error catch
20194  {
20195  AnsiString TTErrorFileName = "Analysis Error.txt";
20196  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
20197  std::ofstream TTError(TTErrorFileName.c_str());
20198  if(TTError == 0)
20199  {
20200  ShowMessage("Analysis error file failed to open - can't be created");
20201  Utilities->CallLogPop(2233);
20202  return(false);
20203  }
20204  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
20205  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
20206  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
20207 
20208  TTError.close();
20209  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
20210  Utilities->CallLogPop(2226);
20211  return(false);
20212  }
20213 }
20214 
20215 // ---------------------------------------------------------------------------
20216 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
20217 {
20218  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
20219  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
20220  {
20221  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
20222  }
20223  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
20224  {
20225  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
20226  AnsiString Marker = "";
20227  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
20228  {
20229  Marker = ',';
20230  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
20231  {
20232  if(int(x) == *MLIt)
20233  {
20234  Marker = "-->,";
20235  break;
20236  }
20237  }
20238  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
20239  if(AVE.FormatType == StartNew)
20240  {
20241  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
20242  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
20243  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
20244  }
20245  if(AVE.FormatType == SNTShuttle)
20246  {
20247  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
20248  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
20249  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
20250  }
20251  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
20252  {
20253  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
20254  }
20255  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
20256  {
20257  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
20258  }
20259  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
20260  {
20261  TActionVectorEntry AVHolder = AVE;
20262  if(AVE.Command.SubString(1,3) == "chr")
20263  {
20264  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
20265  {
20266  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
20267  AVE.OtherHeadCode = "";
20268  }
20269  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
20270  {
20271  AVE.Command = "Change of service to ";
20272  }
20273  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
20274  {
20275  AVE.Command = "Change to shuttle finishing service";
20276  }
20277  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
20278  {
20279  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
20280  AVE.OtherHeadCode = "";
20281  }
20282  }
20283  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
20284  AVE = AVHolder;
20285  }
20286  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
20287  {
20288  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
20289  }
20290  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
20291  {
20292  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
20293  }
20294  else if(AVE.FormatType == TimeTimeLoc)
20295  {
20296  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
20297  }
20298  else if(AVE.FormatType == PassTime)
20299  {
20300  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
20301  }
20302  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
20303  {
20304  AnsiString ListOfExits = "";
20305  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
20306  {
20307  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
20308  }
20309  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
20310  }
20311  else if(AVE.FormatType == FinRemHere)
20312  {
20313  VecFile << Marker << "Frh" << '\n';
20314  }
20315  }
20316  VecFile << '\n';
20317  }
20318  Utilities->CallLogPop(2318);
20319 }
20320 
20321 // ---------------------------------------------------------------------------
20322 
20323 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
20324 {
20325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + HeadCode);
20326  FoundFlag = false;
20327  FinishType = true;
20328  for(unsigned int x = 0; x < Vector.size(); x++)
20329  {
20330 // AnsiString ThisRef = Vector.at(x).ServiceReference;
20331  if(Vector.at(x).ServiceReference == HeadCode)
20332  {
20333  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
20334  {
20335  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
20336  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20337  {
20338  FinishType = false;
20339  }
20340  }
20341  else
20342  {
20343  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
20344  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20345  {
20346  FinishType = false;
20347  }
20348  }
20349  FoundFlag = true;
20350  Utilities->CallLogPop(2319);
20351  return(Vector.at(x));
20352  }
20353  }
20354  Utilities->CallLogPop(2320);
20355  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
20356 }
20357 
20358 // ---------------------------------------------------------------------------
20359 
20360 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
20361 {
20362 //convert times to integer minutes
20363  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
20364  if((Time1 == "") || (Time2 == ""))
20365  {
20366  Utilities->CallLogPop(2213);
20367  return(false);
20368  }
20369  int Mins = Time1.SubString(4,2).ToInt();
20370  int Hours = Time1.SubString(1,2).ToInt();
20371  int Time1Mins = (Hours * 60) + Mins;
20372  Mins = Time2.SubString(4,2).ToInt();
20373  Hours = Time2.SubString(1,2).ToInt();
20374  int Time2Mins = (Hours * 60) + Mins;
20375  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
20376  {
20377  Utilities->CallLogPop(2214);
20378  return(true);
20379  }
20380  Utilities->CallLogPop(2215);
20381  return(false);
20382 }
20383 
20384 // ---------------------------------------------------------------------------
20385 
20386 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
20387  bool &AnalysisError, int &MaxNumberOfSameDirections)
20388 {
20389  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
20390 
20391  try
20392  {
20393  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
20394  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
20395  int SCPos = 0;
20396  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
20397  //first change every second comma in Input to a semicolon so can separate services but keep times with services
20398  bool EvenComma = false;
20399  for(int x = 1; x <= Input.Length(); x++)
20400  {
20401  TempStr1 = Input[x];
20402  if(TempStr1 == AnsiString(',') && EvenComma)
20403  {
20404  TempStr2 += ';';
20405  }
20406  else
20407  {
20408  TempStr2 += Input[x];
20409  }
20410  if(TempStr1 == AnsiString(','))
20411  {
20412  EvenComma = !EvenComma;
20413  }
20414  }
20415  //load up the list of services with associated times
20416  while(TempStr2.Length() > 0)
20417  {
20418  SCPos = TempStr2.Pos(';');
20419  if(SCPos > 0) //0 if not found, as won't be when only one service left
20420  {
20421  OneService = TempStr2.SubString(1, SCPos - 1);
20422  ServiceList.push_back(OneService);
20423  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
20424  }
20425  else //no semicolon so looking at last (or only) element
20426  {
20427  ServiceList.push_back(TempStr2);
20428  TempStr2 = "";
20429  }
20430  }
20431  ServiceList.sort(); // alphabetical order
20432  ServiceList.unique(); //remove duplicates
20433  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20434 
20435  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
20436  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
20437  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
20438  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
20439 
20440  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20441  {
20442  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
20443  }
20444  SLIt3 = ServiceList.end();
20445  SLIt3--; //so points to last element
20446  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
20447  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
20448  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
20449  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
20450  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
20451  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
20452 
20453  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
20454  {
20455  SLIt = SLIt1;
20456  SLIt++; //so points to one after SLIt1
20457  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
20458  {
20459  continue; //already allocated so skip to the next
20460  }
20461  else
20462  {
20463  CommaPos1 = SLIt1->Pos(','); //can't be 0
20464  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
20465  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
20466  SpacePos = ServiceRef1.Pos(' ');
20467  RepeatNum1 = 0;
20468  if(SpacePos > 0) //otherwise it's already correct
20469  {
20470  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20471  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
20472  if(RepeatInfo1[1] == 'F')
20473  {
20474  RepeatNum1 = 0;
20475  }
20476  else
20477  {
20478  SpacePos = RepeatInfo1.Pos(' ');
20479  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
20480  }
20481  }
20482  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
20483  //but this includes the "&0" etc so need to strip these
20484  AmpersandPos = AnsiTime1.Pos('&');
20485  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
20486 
20487  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
20488  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
20489  {
20490  throw Exception("ASCLIt1 Error in " + Input);
20491  }
20492  ServiceCallingLocsList1 = ASCLIt1->second;
20493  AmpersandPos = SLIt1->Pos('&');
20494  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20495  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
20496 
20497  SameDirectionCount = 1;
20498  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
20499  {
20500  CommaPos2 = SLIt2->Pos(','); //can't be 0
20501  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
20502  //but this contains "(First service..." etc so need to strip these
20503  SpacePos = ServiceRef2.Pos(' ');
20504  RepeatNum2 = 0;
20505  if(SpacePos > 0) //otherwise it's already correct
20506  {
20507  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20508  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
20509  if(RepeatInfo2[1] == 'F')
20510  {
20511  RepeatNum2 = 0;
20512  }
20513  else
20514  {
20515  SpacePos = RepeatInfo2.Pos(' ');
20516  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
20517  }
20518  }
20519  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
20520  //but this includes the "&0" etc so need to strip these
20521  AmpersandPos = AnsiTime2.Pos('&');
20522  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
20523 
20524  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
20525  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
20526  {
20527  throw Exception("ASCLIt2 Error in " + Input);
20528  }
20529  ServiceCallingLocsList2 = ASCLIt2->second;
20530  //now compare the two
20531  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
20532  {
20533  int AmpersandPos = SLIt2->Pos('&');
20534  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20535  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
20536  SameDirectionCount++;
20537  }
20538  }
20539  if(SameDirectionCount > MaxNumberOfSameDirections)
20540  {
20541  MaxNumberOfSameDirections = SameDirectionCount;
20542  }
20543  }
20544  }
20545 
20546  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
20547  {
20548  //any existing direction so allocate it now
20549  AmpersandPos = SLIt3->Pos('&');
20550  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20551  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
20552  }
20553  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
20554  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20555  {
20556  //extract the DirectionMarker as an integer
20557  AmpersandPos = SLIt->Pos('&');
20558  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
20559  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
20560  DirectionMarker = DirectionMarkerString.ToInt();
20561  AnsiString DirectionSuffix = "";
20562  char c;
20563  if(DirectionMarker < 27)
20564  {
20565  c = 64 + DirectionMarker; //so 1 -> 'A'
20566  DirectionSuffix = "," + AnsiString(c);
20567  }
20568  else if(DirectionMarker < 53)
20569  {
20570  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
20571  DirectionSuffix = ",A" + AnsiString(c);
20572  }
20573  else
20574  {
20575  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
20576  }
20577  *SLIt = ServiceWithoutMarker + DirectionSuffix;
20578  }
20579  //now prepare the final consolidated output
20580  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20581  {
20582  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20583  }
20584  if(Output.Length() > 0)
20585  {
20586  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20587  }
20588  Utilities->CallLogPop(2216);
20589  return(Output);
20590  }
20591 
20592  catch(const Exception &e) //non error catch
20593  {
20594  AnalysisError = true;
20595  Utilities->CallLogPop(2227);
20596  return(e.Message);
20597  }
20598 }
20599 
20600 // ---------------------------------------------------------------------------
20601 
20602 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
20603 {
20604  //similar to above but doesn't include times in the input
20605  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
20606  AnsiString InternalInput = Input, Output = "", OneService = "";
20607  int CommaPos = 0;
20608  std::list<AnsiString> ServiceList;
20609  //load up the list
20610  while(InternalInput.Length() > 0)
20611  {
20612  CommaPos = InternalInput.Pos(',');
20613  if(CommaPos > 0) //0 if not found, as won't be when only one service left
20614  {
20615  OneService = InternalInput.SubString(1, CommaPos - 1);
20616  ServiceList.push_back(OneService);
20617  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
20618  }
20619  else //no comma so looking at last (or only) element
20620  {
20621  ServiceList.push_back(InternalInput);
20622  InternalInput = "";
20623  }
20624  }
20625 
20626  ServiceList.sort(); // alphabetical order
20627  ServiceList.unique(); //remove duplicates
20628  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20629  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20630  {
20631  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20632  }
20633  if(Output.Length() > 0)
20634  {
20635  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20636  }
20637  Utilities->CallLogPop(2217);
20638  return(Output);
20639 }
20640 
20641 // ---------------------------------------------------------------------------
20642 
20643 
20644 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
20645  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
20646 {
20647  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
20648  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
20649 
20650  std::list<AnsiString>::iterator LP1, LP2, ListPtr1, ListPtr2, LocPtr1, LocPtr2; //LP1 & 2 are temporary pointers, ListPtrs are
20651  //general list pointers, LocPtrs point to Location in the two lists
20652 
20653  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
20654  //for List1
20655  bool LocFound = false;
20656  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
20657  int IncMinutes;
20658  TDateTime FirstServiceTime;
20659 
20660  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
20661  int Ref1Target = 0, Ref1Count = 0;
20662  int Ref2Target = 0, Ref2Count = 0;
20663 
20664 /* drop this after retained slashes in ServiceRef
20665  int SlashPos = Ref1.Pos('/');
20666  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
20667  {
20668  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
20669  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20670  }
20671  int Ref2Target = 0, Ref2Count = 0;
20672  SlashPos = Ref2.Pos('/');
20673  if(SlashPos > 0) //if 0 leave as is
20674  {
20675  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
20676  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20677  }
20678 */
20679 
20680  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
20681  {
20682  //even if others have same names. But if there are cdt's then need to refind the correct service
20683  if((*ListPtr1) == Location) //
20684  {
20685  LocPtr1 = ListPtr1; //may be modified later
20686  LocFound = true;
20687  }
20688  if(ListPtr1->SubString(1, 3) == "%%%")
20689  {
20690  AnsiString CDTTime = ListPtr1->SubString(4, 5);
20691  //now adjust the time to correspond to the repeat if there is one
20692  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
20693  {
20694  IncMinutes = -1;
20695  FirstServiceTime = TDateTime(-1);
20696  bool BreakFlag = false;
20697  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20698  {
20699  if(TDVIt->ServiceReference == Ref1)
20700  {
20701  if(Ref1Target > Ref1Count)
20702  {
20703  Ref1Count++;
20704  continue;
20705  }
20706  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20707  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20708  {
20709  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20710  {
20711  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
20712  BreakFlag = true;
20713  break;
20714  }
20715  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
20716  {
20717  FirstServiceTime = AVIt->ArrivalTime;
20718  BreakFlag = true;
20719  break;
20720  }
20721  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20722  {
20723  FirstServiceTime = AVIt->DepartureTime;
20724  BreakFlag = true;
20725  break;
20726  }
20727  }
20728  if(BreakFlag)
20729  {
20730  break;
20731  }
20732  }
20733  }
20734  if(IncMinutes == -1)
20735  {
20736  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20737  }
20738  if(FirstServiceTime == TDateTime(-1))
20739  {
20740  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20741  }
20742  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
20743  }
20744  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
20745  {
20746  LocFound = false;
20747  continue;
20748  }
20749  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
20750  {
20751  break;
20752  }
20753  if(Time1 > CDTTime) //not there yet so go on
20754  {
20755  LocFound = false;
20756  continue;
20757  }
20758  if(Time1 < CDTTime) //gone too far so can stop now
20759  {
20760  break;
20761  }
20762  }
20763  }
20764  if(!LocFound) //have to find it in both lists
20765  {
20766  Utilities->CallLogPop(2228);
20767  return( false);
20768  }
20769  //for List2
20770  LocFound = false;
20771  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
20772  {
20773  if((*ListPtr2) == Location)
20774  {
20775  LocPtr2 = ListPtr2; //may be modified later
20776  LocFound = true;
20777  }
20778  if(ListPtr2->SubString(1, 3) == "%%%")
20779  {
20780  AnsiString CDTTime = ListPtr2->SubString(4, 5);
20781  //now adjust the time to correspond to the repeat if there is one
20782  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
20783  {
20784  IncMinutes = -1;
20785  FirstServiceTime = TDateTime(-1);
20786  bool BreakFlag = false;
20787  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20788  {
20789  if(TDVIt->ServiceReference == Ref2)
20790  {
20791  if(Ref2Target > Ref2Count)
20792  {
20793  Ref2Count++;
20794  continue;
20795  }
20796  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20797  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20798  {
20799  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20800  {
20801  FirstServiceTime = AVIt->EventTime;
20802  BreakFlag = true;
20803  break;
20804  }
20805  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
20806  {
20807  FirstServiceTime = AVIt->ArrivalTime;
20808  BreakFlag = true;
20809  break;
20810  }
20811  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20812  {
20813  FirstServiceTime = AVIt->DepartureTime;
20814  BreakFlag = true;
20815  break;
20816  }
20817  }
20818  if(BreakFlag)
20819  {
20820  break;
20821  }
20822  }
20823  }
20824  if(IncMinutes == -1)
20825  {
20826  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20827  }
20828  if(FirstServiceTime == TDateTime(-1))
20829  {
20830  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20831  }
20832  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
20833  }
20834  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
20835  {
20836  LocFound = false;
20837  continue;
20838  }
20839  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
20840  {
20841  break;
20842  }
20843  if(Time2 > CDTTime) //not there yet so go on
20844  {
20845  LocFound = false;
20846  continue;
20847  }
20848  if(Time2 < CDTTime) //gone too far so can stop now
20849  {
20850  break;
20851  }
20852  }
20853  }
20854  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
20855  {
20856  Utilities->CallLogPop(2229);
20857  return( false);
20858  }
20859  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
20860  //set ListPtr1 to the search start position
20861  if(Arrival)
20862  {
20863  LP1 = List1.begin();
20864  LP1--; //now points to before the first entry
20865  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
20866  {
20867  if(ListPtr1 == List1.begin())
20868  {
20869  break;
20870  }
20871  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
20872  {
20873  ListPtr1++; //point to one past the cdt
20874  break;
20875  }
20876  }
20877  //set ListPtr2 to the search start position
20878  LP2 = List2.begin();
20879  LP2--; //now points to before the first entry
20880  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
20881  {
20882  if(ListPtr2 == List2.begin())
20883  {
20884  break;
20885  }
20886  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
20887  {
20888  ListPtr2++; //point to one past the cdt
20889  break;
20890  }
20891  }
20892  //ListPtr1 & 2 now at search start position
20893  LP1 = ListPtr1;
20894  LP2 = ListPtr2;
20895  //now search forwards, i.e. for common locations before Location
20896  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20897  {
20898  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
20899  {
20900  break;
20901  }
20902  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
20903  {
20904  break;
20905  }
20906  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20907  {
20908  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
20909  {
20910  break;
20911  }
20912  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
20913  {
20914  break;
20915  }
20916  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
20917  {
20918  Utilities->CallLogPop(2230);
20919  return( true);
20920  }
20921  }
20922  }
20923  }
20924 
20925  //now, for the departure analysis, reset the start positions and search locations after Location
20926 
20927  else
20928  {
20929  LP1 = LocPtr1;
20930  LP1++; //start at one past the location itself
20931  LP2 = LocPtr2;
20932  LP2++;
20933  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20934  {
20935  if(ListPtr1 == List1.end()) //reached end point so stop
20936  {
20937  break;
20938  }
20939  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
20940  {
20941  break;
20942  }
20943  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20944  {
20945  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
20946  {
20947  break;
20948  }
20949  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
20950  {
20951  break;
20952  }
20953  if((*ListPtr1) == (*ListPtr2)) //found a common later location
20954  {
20955  Utilities->CallLogPop(2231);
20956  return( true);
20957  }
20958  }
20959  }
20960  }
20961  Utilities->CallLogPop(2232);
20962  return( false);
20963 }
20964 
20965 // ---------------------------------------------------------------------------
20966 
20967 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
20968 {
20969  // changed at v2.7.0 to show allowable exit elements
20970  if(ExitList.empty())
20971  {
20972  return("");
20973  }
20974  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
20975  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
20976  AnsiString ExitLocList = "";
20977  AllowedExits = "";
20978 
20979  unsigned int Counter = 0;
20980  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
20981  {
20982  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
20983  Counter++;
20984  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
20985  {
20986  ExitLocList += "\n";
20987  }
20988  }
20989  if(StartName == "")
20990  {
20991  if(ExitList.size() == 1)
20992  {
20993  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
20994  Utilities->CallLogPop(1571);
20995  return(" at " + ID);
20996  }
20997  else
20998  {
20999  Utilities->CallLogPop(1572);
21000  if(ExitList.size() < 4)
21001  {
21002  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
21003  return("");
21004  }
21005  else
21006  {
21007  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
21008  return("");
21009  }
21010  }
21011  }
21012  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
21013  {
21014  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
21015  {
21016  Utilities->CallLogPop(1570);
21017  if(ExitList.size() < 4)
21018  {
21019  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
21020  return("");
21021  }
21022  else
21023  {
21024  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
21025  return("");
21026  }
21027  }
21028  }
21029  Utilities->CallLogPop(1569);
21030  if(ExitList.size() < 4)
21031  {
21032  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
21033  return(" at " + StartName);
21034  }
21035  else
21036  {
21037  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
21038  return(" at " + StartName);
21039  }
21040 }
21041 
21042 // ---------------------------------------------------------------------------
21043 /* can't trust this as locations within a vector may not be contiguous
21044  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
21045  {
21046  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
21047  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
21048  //must be preceded by a TimeLoc departure
21049  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
21050  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
21051  {
21052  if((AVPtr + x) < TDEPtr->ActionVector.end())
21053  {
21054  AnsiString xx = (AVPtr + x)->Command;//test
21055  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
21056  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
21057  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
21058  {
21059  Utilities->CallLogPop();
21060  return false;
21061  }
21062  else if((AVPtr + x)->SequenceType == FinishSequence)
21063  {
21064  Utilities->CallLogPop();
21065  return true;
21066  }
21067  }
21068  }
21069  Utilities->CallLogPop();
21070  return false;
21071  }
21072 */
21073 // ---------------------------------------------------------------------------
21074 
21075 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
21076 {
21077  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
21078  AnsiString FormatStr = "####0.0";
21079  AnsiString AvLateArrMins = "";
21080  AnsiString AvEarlyArrMins = "";
21081  AnsiString AvLatePassMins = "";
21082  AnsiString AvEarlyPassMins = "";
21083  AnsiString AvLateDepMins = "";
21084  AnsiString AvLateExitMins = "";
21085  AnsiString AvEarlyExitMins = "";
21086 
21087  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
21088  for(unsigned int x = 0; x < TrainVector.size(); x++)
21089  {
21090  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
21091  }
21092 
21093  if(LateArrivals > 0)
21094  {
21095  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
21096  }
21097  if(EarlyArrivals > 0)
21098  {
21099  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
21100  }
21101  if(LatePasses > 0)
21102  {
21103  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
21104  }
21105  if(EarlyPasses > 0)
21106  {
21107  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
21108  }
21109  if(LateDeps > 0)
21110  {
21111  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
21112  }
21113  if(LateExits > 0) //added at v2.9.1
21114  {
21115  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
21116  }
21117  if(EarlyExits > 0) //added at v2.9.1
21118  {
21119  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
21120  }
21121  PerfFile << '\n' << '\n' << "***************************************";
21122  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
21123 
21124  if(OnTimeArrivals != 1)
21125  {
21126  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
21127  }
21128  else
21129  {
21130  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
21131  }
21132  if(LateArrivals > 1)
21133  {
21134  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
21135  }
21136  else if(LateArrivals == 1)
21137  {
21138  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
21139  }
21140  else
21141  {
21142  PerfFile << LateArrivals << " late arrivals" << '\n';
21143  }
21144  if(EarlyArrivals > 1)
21145  {
21146  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
21147  }
21148  else if(EarlyArrivals == 1)
21149  {
21150  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
21151  }
21152  else
21153  {
21154  PerfFile << EarlyArrivals << " early arrivals" << '\n';
21155  }
21156  if(OnTimePasses != 1)
21157  {
21158  PerfFile << OnTimePasses << " on-time passes" << '\n';
21159  }
21160  else
21161  {
21162  PerfFile << OnTimePasses << " on-time pass" << '\n';
21163  }
21164  if(LatePasses > 1)
21165  {
21166  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
21167  }
21168  else if(LatePasses == 1)
21169  {
21170  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
21171  }
21172  else
21173  {
21174  PerfFile << LatePasses << " late passes" << '\n';
21175  }
21176  if(EarlyPasses > 1)
21177  {
21178  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
21179  }
21180  else if(EarlyPasses == 1)
21181  {
21182  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
21183  }
21184  else
21185  {
21186  PerfFile << EarlyPasses << " early passes" << '\n';
21187  }
21188 
21189  if(OnTimeExits != 1) //this batch added at v2.9.1
21190  {
21191  PerfFile << OnTimeExits << " on-time exits" << '\n';
21192  }
21193  else
21194  {
21195  PerfFile << OnTimeExits << " on-time exit" << '\n';
21196  }
21197  if(LateExits > 1)
21198  {
21199  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
21200  }
21201  else if(LateExits == 1)
21202  {
21203  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
21204  }
21205  else
21206  {
21207  PerfFile << LateExits << " late exits" << '\n';
21208  }
21209  if(EarlyExits > 1)
21210  {
21211  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
21212  }
21213  else if(EarlyExits == 1)
21214  {
21215  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
21216  }
21217  else
21218  {
21219  PerfFile << EarlyExits << " early exits" << '\n';
21220  }
21221 
21222  if(OnTimeDeps != 1)
21223  {
21224  PerfFile << OnTimeDeps << " on-time departures" << '\n';
21225  }
21226  else
21227  {
21228  PerfFile << OnTimeDeps << " on-time departure" << '\n';
21229  }
21230  if(LateDeps > 1)
21231  {
21232  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
21233  }
21234  else if(LateDeps == 1)
21235  {
21236  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
21237  }
21238  else
21239  {
21240  PerfFile << LateDeps << " late departures" << '\n';
21241  }
21242  TDateTime TempExcessLCDownTime;
21243  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
21244  {
21245 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
21246  //later perf summaries with lower values, changed at v2.8.0
21247 // {
21248  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
21249 // }
21250 /*
21251  else
21252  {
21253  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
21254  }
21255 */
21256  if(TempExcessLCDownTime > TDateTime(0))
21257  {
21258  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
21259  }
21260  }
21261 
21262  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
21263 
21264  if(ExcessLCDownMins > 0.1)
21265  {
21266  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
21267  }
21268  else
21269  {
21270  ExcessLCDownMins = 0; //added at v2.16.1 so doesn't count towards performance score if < 0.1mins (else can have low score with no excess mins recorded)
21271  }
21272  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
21273  {
21274  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
21275  }
21276  if(MissedStops != 1)
21277  {
21278  PerfFile << MissedStops << " missed stops" << '\n';
21279  }
21280  else
21281  {
21282  PerfFile << MissedStops << " missed stop" << '\n';
21283  }
21284  if(OtherMissedEvents != 1)
21285  {
21286  PerfFile << OtherMissedEvents << " other missed events" << '\n';
21287  }
21288  else
21289  {
21290  PerfFile << OtherMissedEvents << " other missed event" << '\n';
21291  }
21292  if(SkippedTTEvents != 1)
21293  {
21294  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
21295  }
21296  else
21297  {
21298  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
21299  }
21300  if(UnexpectedExits != 1)
21301  {
21302  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
21303  }
21304  else
21305  {
21306  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
21307  }
21308  if(IncorrectExits != 1)
21309  {
21310  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
21311  }
21312  else
21313  {
21314  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
21315  }
21316  if(NumFailures != 1)
21317  {
21318  PerfFile << NumFailures << " train failures" << '\n';
21319  }
21320  else
21321  {
21322  PerfFile << NumFailures << " train failure" << '\n';
21323  }
21324  if(AvHoursIntValue > 0)
21325  {
21326  if(AvHoursIntValue == 1)
21327  {
21328  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
21329  }
21330  else
21331  {
21332  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
21333  }
21334  }
21335  AnsiString AvLateMinsLocsNotReached = "";
21336 
21338  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
21339  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
21340 
21341  if(LocsNotReached > 0)
21342  {
21343  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
21344  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
21345  }
21346  if(SPADRisks != 1)
21347  {
21348  PerfFile << SPADRisks << " SPAD risks" << '\n';
21349  }
21350  else
21351  {
21352  PerfFile << SPADRisks << " SPAD risk" << '\n';
21353  }
21354  if(SPADEvents != 1)
21355  {
21356  PerfFile << SPADEvents << " SPADs" << '\n';
21357  }
21358  else
21359  {
21360  PerfFile << SPADEvents << " SPAD" << '\n';
21361  }
21362  if(Derailments != 1)
21363  {
21364  PerfFile << Derailments << " derailments" << '\n';
21365  }
21366  else
21367  {
21368  PerfFile << Derailments << " derailment" << '\n';
21369  }
21370  if(CrashedTrains != 1)
21371  {
21372  PerfFile << CrashedTrains << " crashed trains" << '\n';
21373  }
21374  else
21375  {
21376  PerfFile << CrashedTrains << " crashed train" << '\n';
21377  }
21378  PerfFile << '\n' << "***************************************" << '\n';
21379 
21380  bool DerailSPADFlag = false, CrashFlag = false;
21381 
21382  int OverallScorePercent = 100;
21383  int TotArrDepExit = 0;
21384  double TotLateMinsFactor = 1;
21385  double MissedStopAndSPADRiskFactor = 1;
21386  double NetNegFactor = 1;
21387 
21389  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
21390  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
21391  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
21392  // 'no timetabled departures... message, which was inappropriate
21393 
21394  if((SPADEvents > 0) || (Derailments > 0))
21395  {
21396  OverallScorePercent = 5; // overrides other calculations
21397  DerailSPADFlag = true;
21398  }
21399  if(CrashedTrains > 0)
21400  {
21401  OverallScorePercent = 0; // overrides other calculations
21402  CrashFlag = true;
21403  }
21404  if(OverallScorePercent == 100)
21405  {
21406  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
21407  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
21408  {
21409  LatenessPenalty = 0;
21410  }
21411  else
21412  {
21413  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
21414  }
21415  if(TotArrDepExit > 0)
21416  {
21417  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
21418  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
21419  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
21420  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
21421  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
21422  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
21423  // of arrivals & departures, where 4% = half, 8% = a quarter etc
21424  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
21425  // NetNegfactor: product of the above two
21426  OverallScorePercent = 100 * NetNegFactor;
21427  }
21428  }
21429  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
21430  // flag condits added at v1.1.4 - see above for what the error was
21431  {
21432  AnsiString OneFailureString = ", though the failure would account for some poor performance";
21433  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
21434  AnsiString AddedString = "";
21435  if(NumFailures == 1)
21436  {
21437  AddedString = OneFailureString;
21438  }
21439  if(NumFailures > 1)
21440  {
21441  AddedString = TwoOrMoreFailureString;
21442  }
21443  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
21444  AnsiString Rating = "";
21445  if(OverallScorePercent == 100)
21446  {
21447  Rating = "Perfect!";
21448  }
21449  else if(OverallScorePercent >= 95)
21450  {
21451  Rating = "Excellent";
21452  }
21453  else if(OverallScorePercent >= 90)
21454  {
21455  Rating = "Very good";
21456  }
21457  else if(OverallScorePercent >= 80)
21458  {
21459  Rating = "Good";
21460  }
21461  else if(OverallScorePercent >= 70)
21462  {
21463  Rating = "Fair";
21464  }
21465  else if(OverallScorePercent >= 60)
21466  {
21467  Rating = "Unacceptable" + AddedString;
21468  }
21469  else if(OverallScorePercent >= 50)
21470  {
21471  Rating = "Poor" + AddedString;
21472  }
21473  else if(OverallScorePercent >= 40)
21474  {
21475  Rating = "Bad" + AddedString;
21476  }
21477  else if(OverallScorePercent >= 30)
21478  {
21479  Rating = "Very bad" + AddedString;
21480  }
21481  else if(OverallScorePercent >= 20)
21482  {
21483  Rating = "Terrible" + AddedString;
21484  }
21485  else if(OverallScorePercent >= 10)
21486  {
21487  Rating = "Appalling" + AddedString;
21488  }
21489  else if(OverallScorePercent >= 5)
21490  {
21491  if(DerailSPADFlag)
21492  {
21493  Rating = "Disastrous - potential loss of life";
21494  }
21495  // SPADs/Derailments
21496  else
21497  {
21498  Rating = "Dire" + AddedString;
21499  }
21500  }
21501  else if(OverallScorePercent < 5)
21502  {
21503  if(CrashFlag)
21504  {
21505  Rating = "Catastrophic - loss of life"; // Crashes
21506  }
21507  else
21508  {
21509  Rating = "Abysmal";
21510  }
21511  }
21512  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
21513  }
21514  else
21515  {
21516  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
21517  }
21518  PerfFile << '\n' << "***************************************";
21519  PerfFile.flush();
21520  Utilities->CallLogPop(1736);
21521 }
21522 
21523 // ---------------------------------------------------------------------------
21524 
21526 {
21527  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
21528  for(unsigned int x = 0; x < TrainVector.size(); x++)
21529  {
21530  TTrain &Train = TrainVectorAt(58, x);
21531  if(Train.Crashed)
21532  // can't use background colours for crashed & derailed because same colour
21533  {
21534  CrashWarning = true;
21535  }
21536  else if(Train.Derailed)
21537  // can't use background colours for crashed & derailed because same colour
21538  {
21539  DerailWarning = true;
21540  }
21541  else if(Train.BackgroundColour == clSPADBackground)
21542  // use colour as that changes as soon as passes signal
21543  {
21544  SPADWarning = true;
21545  }
21546  else if(Train.BackgroundColour == clTrainFailedBackground)
21547  {
21548  TrainFailedWarning = true;
21549  }
21550  else if(Train.BackgroundColour == clCallOnBackground)
21551  // use colour as also stopped at signal
21552  {
21553  CallOnWarning = true;
21554  }
21555  else if(Train.BackgroundColour == clSignalStopBackground)
21556  // use colour to distinguish from call-on
21557  {
21558  SignalStopWarning = true;
21559  }
21560  else if(Train.BackgroundColour == clBufferAttentionNeeded)
21561  // use colour to distinguish from ordinary buffer stop
21562  {
21563  BufferAttentionWarning = true;
21564  }
21565  }
21566  Utilities->CallLogPop(1796);
21567 }
21568 
21569 // ---------------------------------------------------------------------------
21570 
21572 {
21573  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
21574 
21575  // calculate lateness for running trains
21578  for(unsigned int x = 0; x < TrainVector.size(); x++)
21579  {
21580  TTrain &Train = TrainVectorAt(64, x);
21581  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
21582  AVEntryPtr++)
21583  {
21584  if(AVEntryPtr < Train.ActionVectorEntryPtr)
21585  {
21586  continue;
21587  }
21588  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21589  TTClockTime))
21590  {
21591  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
21593  }
21594 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
21595  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21596  TTClockTime))
21597  {
21598  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
21599  OperatingTrainArrDep++;
21600  }
21601 */
21602  }
21603  }
21604 
21605  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
21608 
21609  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
21610  {
21611  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
21612  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
21613  int IncrementalMinutes = 0;
21614  if(AVEntryLast.FormatType == Repeat)
21615  {
21616  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
21617  }
21618  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
21619  {
21620  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
21621  if(TTOD.RunningEntry != NotStarted)
21622  {
21623  continue;
21624  }
21625  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
21626  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
21627  bool TrainOperatingFlag = false;
21628  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
21629  {
21630  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
21631  {
21632  TrainOperatingFlag = true;
21633  break;
21634  }
21635  }
21636  if(TrainOperatingFlag)
21637  {
21638  continue;
21639  }
21640  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
21641  {
21642  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
21643  }
21644  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
21645  {
21646  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
21647  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
21648  {
21649  break; // all the rest will also be greater (& default of -1 will be less)
21650  }
21651  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
21652  {
21653  break; // all the rest will also be greater (& default of -1 will be less)
21654  }
21655  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
21656  {
21657  break; // all the rest will also be greater (& default of -1 will be less)
21658  }
21659  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
21660  {
21661  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
21663  }
21664 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
21665  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
21666  {
21667  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
21668  NotStartedTrainArrDep++;
21669  }
21670 */
21671  }
21672  }
21673  }
21674  Utilities->CallLogPop(1894);
21675 }
21676 
21677 // ---------------------------------------------------------------------------
21678 
21680 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
21681 // clears entries then adds values for running trains then for continuation entries
21682 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
21683 {
21684  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
21685  OpTimeToActMultiMap.clear();
21686  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
21687 
21688  if(!TrainVector.empty())
21689  // build OpTimeToActMultiMap entries for running trains
21690  {
21691  AnsiString HeadCode;
21692  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
21693  int TrainID;
21694  THCandTrainPosParam HCandTrainPosParam;
21695  for(unsigned int x = 0; x < TrainVector.size(); x++)
21696  {
21697  HeadCode = TrainVectorAt(62, x).HeadCode;
21698  TrainID = TrainVectorAt(63, x).TrainID;
21699  HCandTrainPosParam.first = HeadCode;
21700  HCandTrainPosParam.second = TrainID;
21701  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
21702  if((TimeToAct >= 0) && (TimeToAct < 59.9))
21703  // -1 indicates don't display
21704  {
21705  OpTimeToActMultiMapEntry.first = TimeToAct;
21706  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21707  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21708  }
21709  }
21710  }
21711 /*
21712  * class TContinuationTrainExpectationEntry
21713  {
21714  public:
21715  AnsiString Description; ///< service description
21716  AnsiString HeadCode; ///< service headcode
21717  int RepeatNumber; ///< service RepeatNumber
21718  int IncrementalMinutes; ///< Repeat separation in minutes
21719  int IncrementalDigits; ///< Repeat headcode separation
21720  int VectorPosition; ///< TrackVectorPosition for the continuation element
21721  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
21722  };
21723 
21724  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
21725  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
21726  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
21727  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
21728 */
21729 
21731  // build OpTimeToActMultiMap entries for expected trains
21732  {
21733  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
21734  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
21735  float TimeToAct = 0; // minutes
21736  int DistanceToRedSignal = 0; // metres
21737  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
21738  // used to ensure only one train displayed for a given continuation
21739  ContinuationEntryVecPosVector.clear();
21740  bool LaterTrain = false;
21743  {
21744  LaterTrain = false;
21745  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
21746  {
21747  CTEIt++;
21748  continue; // not interested in running or exited trains
21749  }
21750  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
21751  {
21752  CTEIt++;
21753  continue;
21754  // don't include trains not entered yet when a train is already on the continuation
21755  }
21756  if(!ContinuationEntryVecPosVector.empty())
21757  {
21758  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
21759  {
21760  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
21761  {
21762  LaterTrain = true;
21763  ;
21764  // skip past remaining trains waiting to enter at same point
21765  break;
21766  }
21767  }
21768  }
21769  if(LaterTrain)
21770  {
21771  CTEIt++;
21772  continue;
21773  }
21774  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
21775  AnsiString HeadCode = CTEIt->second.HeadCode;
21776  float CurrentStopTime; // set to 0 at start of function
21777  float LaterStopTime; // set to 0 at start of function
21778  float RecoverableTime; // set to 0 at start of function
21779  int AvTrackSpeed; // set to 0 at start of function
21780  int TrainID = -1; // not yet allocated for train still to enter
21781  int DistanceToExit; //not used for continuation entries
21782  THVShortPair ExitPair;
21783  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
21784 
21785 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
21786 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
21787 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
21788 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
21789 
21790  int AtValue = 1;
21791  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
21792  {
21793  AtValue = 0;
21794  }
21795  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
21796  // EntryPos always 0 for entering at a continuation
21797  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
21798  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
21799  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
21800  // for a train it's the one in front of LeadElement
21801  if(AvTrackSpeed < 30)
21802  {
21803  AvTrackSpeed = 30;
21804  }
21805  if(DistanceToRedSignal == -1)
21806  {
21807  TimeToAct = 60.0;
21808  }
21809  else
21810  {
21811  int Speed = AvTrackSpeed;
21812  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
21813  if(AvTrackSpeed > MaxSpeed)
21814  {
21815  Speed = MaxSpeed;
21816  }
21817  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
21818  // defined in timetable as under signaller control
21819  {
21820  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
21821  LaterStopTime = 0;
21822  }
21823  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
21824  // accel & decel taken into account in
21825  // CalcDistanceToRedSignalandStopTime
21826  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
21827  // don't need CurrentStopTime or RecoverableTime for continuation entries
21828  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
21829  TimeToAct += MinsBefEnter;
21830  }
21831  THCandTrainPosParam HCandTrainPosParam;
21832  HCandTrainPosParam.first = HeadCode;
21833  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
21834  // -1-CTE... because 2nd value covers TrainID if +ve &
21835  // continuation track vector position if -ve, -1 allows for vecpos being 0
21836  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
21837  {
21838  OpTimeToActMultiMapEntry.first = TimeToAct;
21839  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21840  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21841  }
21842  CTEIt++;
21843  }
21844  }
21845  Utilities->CallLogPop(2081);
21846 }
21847 
21848 // ---------------------------------------------------------------------------
21849 
21851 // new for multiplayer
21852 // clears entries then adds values for running trains
21853 {
21854  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
21855  TimeToExitMultiMap.clear();
21856  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
21857 
21858  if(!TrainVector.empty())
21859  // build map entries for running trains
21860  {
21861  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
21862  THVShortPair ExitPair;
21863  float TimeToExit;
21864  for(unsigned int x = 0; x < TrainVector.size(); x++)
21865  {
21867  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
21868  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
21869  ExitPair = TrainVectorAt(76, x).ExitPair;
21870  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
21871  {
21872  ExitInfo.TimeToExitSecs = -1;
21873  }
21874  TimeToExitMultiMapEntry.first = ExitPair;
21875  TimeToExitMultiMapEntry.second = ExitInfo;
21876  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
21877  }
21878  }
21879  Utilities->CallLogPop(2323);
21880 }
21881 
21882 // ---------------------------------------------------------------------------
21883 
21884 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
21885  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
21886  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
21887 // new v2.2.0
21888 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
21889 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
21890 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
21891 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
21892 // aren't used - this means there is no display for the train in question
21893 {
21894  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
21895  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
21896  int DistanceToRedSignal = 0;
21897  DistanceToExit = -1;
21898  ExitPair.first = -1;
21899  ExitPair.second = -1;
21900  int CumTrackSpeed = 0;
21901  // average track speed, in case need to use in time calc
21902  int TrackSpeedCount = 0;
21903  float KmPerLocationStop;
21904  float MaxAllowableSpeed;
21905 
21906  //below added at v2.6.1
21907  if(TrainID > -1) //will be -1 for trains not entered yet
21908  {
21909  TTrain &Train = TrainVectorAtIdent(51, TrainID);
21910  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
21911  Train.StationStopCalculated = false;
21912  }
21913  AvTrackSpeed = 0;
21914  int CurrentElement = TrackVectorPosition;
21915  int CurrentEntryPos = TrackVectorPositionEntryPos;
21916  int NextElement;
21917  int NextEntryPos;
21918  int NextExitPos;
21919 
21920  CurrentStopTime = 0;
21921  LaterStopTime = 0;
21922  RecoverableTime = 0;
21923  if(CurrentElement == -1) // train on end element, no action needed
21924  {
21925  Utilities->CallLogPop(2094);
21926  return(-1);
21927  }
21928  int CurrentExitPos;
21929 
21930  // get ExitPos for first element to be measured
21931  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
21932  {
21933  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
21934  {
21935  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
21936  {
21937  CurrentExitPos = 1;
21938  }
21939  else
21940  {
21941  CurrentExitPos = 3;
21942  }
21943  }
21944  else
21945  {
21946  CurrentExitPos = 0; // trailing point
21947  }
21948  }
21949  else
21950  {
21951  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
21952  }
21953  // get CumTrackSpeed for first measured element
21954 
21955  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
21956  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
21957  bool CurrentElementFailed = Track->TrackElementAt(1549, CurrentElement).Failed; //added at v2.13.2
21958 
21959  // check if currently stopped at a location, and if so add the remaining dwell time
21960  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
21961  if(TrainID > -1)
21962  // -1 for a continuation and can't be at a location as not yet entered
21963  {
21964  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
21966  // this used to deduct from RecoverableTime when arrive at a location
21967  if(Train.RevisedStoppedAtLoc())
21968  {
21969  if(Train.StoppedForTrainInFront)
21970  {
21971  Utilities->CallLogPop(2082);
21972  return(-1); // no action needed
21973  }
21975  { //added '|| (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)' at v2.16.1 so description ignored in calculating action due time
21976  Utilities->CallLogPop(2083);
21977  return(-1); // not due a departure or a description change so no action needed
21978  }
21979  else if(((Train.ActionVectorEntryPtr + 1)->FormatType == TimeLoc) && (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)) // due a departure immediately after change of description
21980  { //added at v2.16.1 to cover description change due next then a departure
21981  double TimeToDepart = double((Train.GetTrainTime(68, (Train.ActionVectorEntryPtr + 1)->DepartureTime)) - TrainController->TTClockTime) * 86400 / 60; // mins to depart excluding possible 30sec allowance from LastActionTime
21982  //need repeat time for the above
21983  if((Train.ActionVectorEntryPtr + 1)->DepartureTime == Train.ActionVectorEntryPtr->EventTime) //don't need repeat time here
21984  {
21985  TimeToDepart+= 0.5; //add in the 30 secs if depature time same as description change time
21986  }
21987  if(TimeToDepart < 0.5)
21988  {
21989  TimeToDepart = 0.5;
21990  }
21991  // can't convert a TDateTime to a float directly
21992  CurrentStopTime = float(TimeToDepart);
21993  AVPtr++;
21994  AVPtr++;
21995  }
21996  else if((Train.ActionVectorEntryPtr->FormatType == TimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc)) // due a departure as next action
21997  {
21998  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
21999  // can't convert a TDateTime to a float directly
22000  CurrentStopTime = float(TimeToDepart);
22001  AVPtr++;
22002  }
22003  else //added at v2.16.1 to catch all other combinations
22004  { //none of the above so no action needed
22005  Utilities->CallLogPop(2628);
22006  return(-1);
22007  }
22008  }
22009  }
22010  // check if CurrentElement is a red signal, but ok if autosig route after provided signal not failed
22011  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
22012  // ok if autosig route after red signal unless signal has failed
22013  {
22014  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
22015  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
22016  int RouteNumber; // holder for referenced value, not used
22017  if((AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
22018  { //CurrentElementFailed added at v2.13.2
22019  Utilities->CallLogPop(2078);
22020  return(-1);
22021  }
22022  else if(SigControlAndCanPassRedSignal)
22023  // ignore signal and increment CurrentElement to NextElement
22024  {
22025  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
22026  {
22027  if((NextEntryPos == 0) || (NextEntryPos == 2))
22028  // leading entry point
22029  {
22030  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
22031  {
22032  NextExitPos = 1;
22033  }
22034  else
22035  {
22036  NextExitPos = 3;
22037  }
22038  }
22039  else
22040  {
22041  NextExitPos = 0; // trailing entry point
22042  }
22043  }
22044  else
22045  {
22046  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
22047  }
22048  CurrentElement = NextElement;
22049  CurrentEntryPos = NextEntryPos;
22050  CurrentExitPos = NextExitPos;
22051  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
22052  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
22053  }
22054  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
22055  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
22056  {
22057  Utilities->CallLogPop(2084);
22058  return(0);
22059  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
22060  }
22061  }
22062  int LaterStopNumber = 0;
22063  int x = 0;
22064  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
22065 
22066  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
22067  // not red signal next (in fwd direction) so enter loop to calc CumLength
22068  {
22069  x++; // added in v2.4.0 as above
22070  if(x > 5000)
22071  {
22072  Utilities->CallLogPop(2120);
22073  return(-1);
22074  }
22075  if(CurrentEntryPos > 1)
22076  {
22077  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
22078  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
22079  }
22080  else
22081  {
22082  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
22083  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
22084  }
22085  TrackSpeedCount++;
22086 
22087  //added for multiplayer - exiting at a continuation and continuation length already added
22088  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
22089  {
22090  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
22091  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
22092  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
22093  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
22094  //need here as next element will be -1 so will exit before calcs at end
22095  if(TrackSpeedCount > 0)
22096  {
22097  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22098  }
22099  else // shouldn't reach here but include to prevent divide by zero error
22100  {
22101  if(CurrentEntryPos > 1)
22102  {
22103  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
22104  }
22105  else
22106  {
22107  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
22108  }
22109  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22110  }
22111  //calc AvTrackSpeed
22112  if(LaterStopNumber > 0)
22113  {
22114  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22115  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22116  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22117  // average line speed/2 (for half distance accelerating and half decelerating.
22118  }
22119  else
22120  {
22121  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22122  // using linear trendline for accel & decel distance at various speeds
22123  // at half braking, speed never < 60 using this
22124  }
22125  if(AvTrackSpeed > MaxAllowableSpeed)
22126  {
22127  AvTrackSpeed = MaxAllowableSpeed;
22128  }
22129  }
22130 
22131  // added at v2.6.1 to find DistanceToStationStop for trains running early
22132  if(TrainID > -1) //can ignore continuation entries as these don't run early
22133  {
22134  TTrain &Train = TrainVectorAtIdent(52, TrainID);
22135  if(!Train.StationStopCalculated)
22136  {
22137  if(Train.TrainMode == Timetable)
22138  {
22139  bool StopRequired = false;
22140  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
22141  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
22142  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
22143  {
22144  // no need to add in the length of element to CumulativeLength
22145  if(StopRequired)
22146  {
22147  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
22148  Train.StationStopCalculated = true; //don't want to update it with later stops
22149  }
22150  }
22151  }
22152  }
22153  }
22154  // check for train in front, but if on a bridge on other track then ok
22155  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
22156  int TrainOnElement;
22157  if(TE.TrackType != Bridge)
22158  {
22159  TrainOnElement = TE.TrainIDOnElement;
22160  }
22161  else
22162  {
22163  if(CurrentEntryPos > 1)
22164  {
22166  }
22167  else
22168  {
22170  }
22171  }
22172  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
22173  // train in front before red signal
22174  {
22175  Utilities->CallLogPop(2085);
22176  return(-1);
22177  }
22178  // add to stoptime if required
22179  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
22180  {
22181  double StopTimeDouble;
22182  while(AVPtr->FormatType == PassTime)
22183  {
22184  AVPtr++; // skip past any passes
22185  }
22186  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
22187  (AVPtr->FormatType == TimeTimeLoc)))
22188  // stop due here so calc dwell time & advance Ptr
22189  {
22190  if(AVPtr->FormatType == TimeTimeLoc)
22191  {
22192  LaterStopNumber++;
22193  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
22194  if(StopTimeDouble < 0.5)
22195  {
22196  StopTimeDouble = 0.5;
22197  }
22198  // at least 30 secs delay at station
22199  // can't convert a TDateTime to a float directly
22200  LaterStopTime += float(StopTimeDouble);
22201  RecoverableTime += StopTimeDouble - 0.5;
22202  if((LaterStopNumber == 1) && (TrainID > -1))
22203  {
22204  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22205  }
22206  AVPtr++;
22207  }
22208  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
22209  {
22210  if((AVPtr + 1)->FormatType == TimeLoc)
22211  // must be a departure
22212  {
22213  LaterStopNumber++;
22214  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
22215  // can't convert a TDateTime to a float directly //so repeat times not required
22216  if(TrainID > -1) //exclude trains still to enter
22217  {
22218  TTrain &Train = TrainVectorAtIdent(67, TrainID);
22219  if(TTClockTime > Train.GetTrainTime(69, AVPtr->ArrivalTime)) //running late, added at v2.16.1
22220  {
22221  StopTimeDouble = double(Train.GetTrainTime(70, (AVPtr + 1)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
22222  // can't convert a TDateTime to a float directly
22223  }
22224  }
22225  if(StopTimeDouble < 0.5)
22226  {
22227  StopTimeDouble = 0.5;
22228  }
22229  // at least 30 secs delay at station
22230  LaterStopTime += float(StopTimeDouble);
22231  RecoverableTime += StopTimeDouble - 0.5;
22232  if((LaterStopNumber == 1) && (TrainID > -1))
22233  {
22234  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22235  }
22236  AVPtr++;
22237  AVPtr++;
22238  }
22239  else if(((AVPtr + 1)->FormatType == TimeCmdDescription) && ((AVPtr + 2)->FormatType == TimeLoc)) //change of description then departure
22240  { //added at v2.16.1 so description changes ignored in calculating time to act
22241  LaterStopNumber++;
22242  StopTimeDouble = double((AVPtr + 2)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
22243  // can't convert a TDateTime to a float directly //so repeat times not required
22244  if(TrainID > -1) //exclude trains still to enter
22245  {
22246  TTrain &Train = TrainVectorAtIdent(68, TrainID);
22247  if(TTClockTime > Train.GetTrainTime(71, AVPtr->ArrivalTime)) //running late, added at v2.16.1
22248  {
22249  StopTimeDouble = double(Train.GetTrainTime(72, (AVPtr + 2)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
22250  // can't convert a TDateTime to a float directly
22251  }
22252  }
22253  if(StopTimeDouble < 0.5)
22254  {
22255  StopTimeDouble = 0.5;
22256  }
22257  // at least 30 secs delay at station
22258  LaterStopTime += float(StopTimeDouble);
22259  RecoverableTime += StopTimeDouble - 0.5;
22260  if((LaterStopNumber == 1) && (TrainID > -1))
22261  {
22262  TrainVectorAtIdent(69, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22263  }
22264  AVPtr++;
22265  AVPtr++;
22266  AVPtr++;
22267  }
22268  else // does something else at the location so no calculation needed
22269  {
22270  Utilities->CallLogPop(2086);
22271  return(-1);
22272  }
22273  }
22274  }
22275  }
22276  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
22277  if(NextElement == -1) // reached end element, no action needed
22278  {
22279  Utilities->CallLogPop(2077);
22280  return(-1);
22281  }
22282  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
22283  // get NextExitPos
22284  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
22285  {
22286  if((NextEntryPos == 0) || (NextEntryPos == 2))
22287  // leading entry point
22288  {
22289  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
22290  {
22291  NextExitPos = 1;
22292  }
22293  else
22294  {
22295  NextExitPos = 3;
22296  }
22297  }
22298  else
22299  {
22300  NextExitPos = 0; // trailing entry point
22301  }
22302  }
22303  else
22304  {
22305  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
22306  }
22307  CurrentElement = NextElement;
22308  CurrentEntryPos = NextEntryPos;
22309  CurrentExitPos = NextExitPos;
22310  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
22311  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
22312  CurrentElementFailed = Track->TrackElementAt(1550, CurrentElement).Failed; //added at v2.13.2
22313  }
22314  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
22315  // ok if autosig route after red signal, no action needed
22316  {
22317  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
22318  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
22319  int RouteNumber; // holder for referenced value, not used
22320  if((AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
22321  { //CurrentElementFailed added at v2.13.2
22322  Utilities->CallLogPop(2095);
22323  return(-1);
22324  }
22325  }
22326 
22327  if(TrackSpeedCount > 0)
22328  {
22329  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22330  }
22331  else // shouldn't reach here but include to prevent divide by zero error
22332  {
22333  if(CurrentEntryPos > 1)
22334  {
22335  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
22336  }
22337  else
22338  {
22339  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
22340  }
22341  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22342  }
22343 
22344  if(LaterStopNumber > 0)
22345  {
22346  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22347  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22348  }
22349  else
22350  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22351  // average line speed/2 (for half distance accelerating and half decelerating.
22352  {
22353  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22354  // using linear trendline for accel & decel distance at various speeds
22355  // at half braking, speed never < 60 using this
22356  }
22357  if(AvTrackSpeed > MaxAllowableSpeed)
22358  {
22359  AvTrackSpeed = MaxAllowableSpeed;
22360  }
22361  Utilities->CallLogPop(2096);
22362  return(DistanceToRedSignal);
22363 }
22364 
22365 // ---------------------------------------------------------------------------
22366 // end of TTrainController entries
22367 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:9433
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18926
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:133
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:381
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:51
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:16123
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:348
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:347
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:490
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:702
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1737
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:904
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:171
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:15791
TTrainDataEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:206
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6527
TActionVectorEntry::SplitDistribution
AnsiString SplitDistribution
Definition: TrainUnit.h:121
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:369
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:21679
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:812
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:17227
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:92
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:52
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1017
Create
@ Create
Definition: TrainUnit.h:51
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:789
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:19933
Arrive
@ Arrive
Definition: TrainUnit.h:51
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:51
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:346
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:387
Depart
@ Depart
Definition: TrainUnit.h:51
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:721
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:12530
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:719
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:292
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:431
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:803
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:286
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7408
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:52
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:40
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:910
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1666
IntermediateSequence
@ IntermediateSequence
Definition: TrainUnit.h:77
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:729
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:139
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:785
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1678
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:67
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:507
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:750
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:435
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:20694
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:15049
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:369
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1003
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:415
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:986
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:40
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:798
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:342
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:982
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:20967
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:8309
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:389
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:481
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:369
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6659
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:798
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10718
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:322
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:901
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:724
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:16958
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:107
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:856
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1009
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1016
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:19722
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:798
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10913
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:12480
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1658
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9405
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:95
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:42
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:133
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:969
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:41
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:746
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:16973
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:264
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:967
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6450
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:42
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:983
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Return the TrainDataVector entry corresponding to ServiceReference, FinishType is 0 for end of servic...
Definition: TrainUnit.cpp:20323
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:684
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:503
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:463
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10680
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:53
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:496
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10984
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:850
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:792
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:349
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:724
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:375
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:204
LeadMid
@ LeadMid
Definition: TrainUnit.h:297
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:41
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:300
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:840
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:20207
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:15871
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7780
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:21850
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:981
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6680
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1553
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:742
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1023
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:251
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1019
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:214
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:860
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:817
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:732
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:330
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9882
ChangeDescription
@ ChangeDescription
Definition: TrainUnit.h:54
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:17076
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:39
MidLag
@ MidLag
Definition: TrainUnit.h:297
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:897
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:17014
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:51
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:297
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:807
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:350
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:52
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:451
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:731
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:1002
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:19470
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:12004
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:992
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5945
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:510
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:183
TTrain
Definition: TrainUnit.h:303
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:989
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:12023
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:299
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11720
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:42
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:892
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:249
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:874
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:487
GapJump
@ GapJump
Definition: TrackUnit.h:66
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1010
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:843
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:293
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:411
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6401
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:10311
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:369
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1705
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:369
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:295
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:363
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:224
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:705
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:367
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:17055
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:836
TTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:326
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:787
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:40
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:467
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:106
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:720
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:397
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:16778
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1411
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6216
End
@ End
Definition: TrackUnit.h:76
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:72
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:401
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7313
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8957
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:344
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10709
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:304
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:407
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:17138
FailMissedDSC
@ FailMissedDSC
Definition: TrainUnit.h:40
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:125
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:977
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:41
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1027
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:304
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:848
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:837
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:425
SignalPost
@ SignalPost
Definition: TrackUnit.h:66
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:971
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:471
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1682
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:352
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:41
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:13173
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:999
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:206
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5657
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:53
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:298
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:16146
TTrainController::CheckFourthValidityForSplit
bool CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages)
Checks fourth segment in timetable for train splits - percentage mass then '-' then percentage power ...
Definition: TrainUnit.cpp:12435
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:147
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:851
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:773
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:901
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:423
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:75
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:483
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:65
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:846
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:812
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:898
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1925
TExitInfo
Definition: TrainUnit.h:103
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:291
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:51
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:487
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:39
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:844
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:305
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:987
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:296
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:94
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:133
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:487
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:17954
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:965
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:461
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:990
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:11279
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:744
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:369
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:459
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1015
Pass
@ Pass
Definition: TrainUnit.h:53
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:377
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:798
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:758
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:43
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3241
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11432
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:828
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:1002
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:494
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:776
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:882
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:54
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:52
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:975
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:718
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:357
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
Definition: TrainUnit.h:318
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1008
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:823
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:9417
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1011
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
< true if a description is given for the train, if only headcode given for a follow-on service then f...
Definition: TrainUnit.h:210
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:220
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:7164
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:815
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:714
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:18507
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:475
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:16415
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10850
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:310
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3449
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1022
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:469
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5242
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2762
RearSplit
@ RearSplit
Definition: TrainUnit.h:51
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:131
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:53
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:827
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:668
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:972
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:821
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:995
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:135
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10744
TUtilities::FailureMode
TFailureMode FailureMode
specifies whether no failures or minor, moderate or major random failures are to be applied (added at...
Definition: Utilities.h:119
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3173
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:7136
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:16513
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:53
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:893
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:842
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:866
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:383
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:968
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9481
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:95
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:334
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:713
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:9227
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8506
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3165
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:827
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:490
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:40
TTrainController::TrainDataVectorCopy
TTrainDataVector TrainDataVectorCopy
vector containing the internal timetable, the copy is used for conflict analysis only
Definition: TrainUnit.h:880
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:465
NewService
@ NewService
Definition: TrainUnit.h:51
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:41
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:17291
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1513
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:487
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:385
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1665
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:373
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:222
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:218
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:834
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:302
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:101
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1012
Crossover
@ Crossover
Definition: TrackUnit.h:66
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4842
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:76
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:974
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1005
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:795
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19163
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
Definition: TrainUnit.h:880
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:20602
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1666
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:467
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10695
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:332
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:16989
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8793
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:18989
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1024
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2380
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:487
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1687
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:964
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:487
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:756
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:312
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:800
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:51
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:433
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:812
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:994
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:970
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:369
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:443
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1006
Leave
@ Leave
Definition: TrainUnit.h:51
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:853
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:768
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:796
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:457
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:793
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:399
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3146
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:973
FNil
@ FNil
Definition: Utilities.h:43
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:808
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:42
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:42
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:41
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2691
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:455
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:487
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:51
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:518
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7995
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:980
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:17038
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:449
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:275
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:15699
TActionVectorEntry::NewDescription
AnsiString NewDescription
Definition: TrainUnit.h:121
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:748
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6608
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9917
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:16078
StartSequence
@ StartSequence
Definition: TrainUnit.h:77
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7221
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:806
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11283
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:785
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:129
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:18835
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:115
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:798
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:274
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:21571
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:439
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:766
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:262
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:804
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5349
Terminate
@ Terminate
Definition: TrainUnit.h:51
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:812
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:799
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:487
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:501
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:371
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:284
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:899
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:15021
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:16551
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:17314
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:459
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11503
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:727
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:239
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:854
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:427
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2577
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:19846
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:287
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:870
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1652
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:10400
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9970
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:7051
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:864
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
called during FloatingLabelNextString to find the next service departure time & next location (last b...
Definition: TrainUnit.cpp:7845
TTrain::Description
AnsiString Description
needs own HeadCode & description because repeat entries will differ from TrainDataEntry values (Descr...
Definition: TrainUnit.h:326
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:997
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:128
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:520
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:117
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9981
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1014
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:473
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1739
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:812
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:12571
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3645
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3345
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:467
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:21884
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:807
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:53
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1650
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:9374
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3209
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:119
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:830
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:17160
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:265
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:991
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:16492
TDisplay
Definition: DisplayUnit.h:49
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:872
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:488
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4779
TimeCmdDescription
@ TimeCmdDescription
Definition: TrainUnit.h:67
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:277
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:38
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10734
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:52
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:359
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:409
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12671
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:826
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:97
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9995
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:141
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:849
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:906
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:447
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:750
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1720
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:212
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:130
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:50
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:113
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:996
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:226
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:441
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:819
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:909
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:558
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5265
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:469
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route (this will be truncated...
Definition: TrackUnit.h:1656
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:40
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:107
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1666
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:137
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:338
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1020
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:711
Moderate
@ Moderate
Definition: Utilities.h:38
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:709
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:301
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10384
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:897
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:835
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:39
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:355
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:17327
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:769
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:985
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Major
@ Major
Definition: Utilities.h:38
Points
@ Points
Definition: TrackUnit.h:66
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:12076
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:743
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:395
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:829
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:15598
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:40
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:393
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:812
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:852
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:53
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1004
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:39
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:403
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:268
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:505
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:17467
Continuation
@ Continuation
Definition: TrackUnit.h:66
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:798
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:469
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7753
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:55
TTrack::PointFlashFlag
bool PointFlashFlag
< true if a route set through an LC that is closed to trains (& therefore needs to be opened)
Definition: TrackUnit.h:767
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:845
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:479
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:11151
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:365
NoEvent
@ NoEvent
Definition: TrainUnit.h:39
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:39
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:21075
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:715
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3303
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:902
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:53
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:779
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:905
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1018
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10765
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:88
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:328
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:485
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:678
Minor
@ Minor
Definition: Utilities.h:38
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:741
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6872
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:135
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:847
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:492
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:268
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:42
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrainDataEntry::ExplicitDescription
bool ExplicitDescription
< headcode is the first train's headcode, rest are calculated from repeat information; ServiceReferen...
Definition: TrainUnit.h:208
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:512
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:53
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:1001
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:20524
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:518
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:903
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:993
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:11141
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:314
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:285
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:709
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:20216
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:716
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:720
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:9337
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:798
TTrain::NextTrainID
static int NextTrainID
< km/h
Definition: TrainUnit.h:323
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:765
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:184
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:726
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:988
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:19620
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:19576
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:445
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:802
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:121
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:281
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:42
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:40
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:155
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:909
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:336
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:50
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:720
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:369
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:839
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1025
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10351
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:16439
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:8061
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:18848
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6929
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:419
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:40
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5710
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:20360
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10369
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:145
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:15028
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:345
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:17120
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:182
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:71
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:15149
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:936
TTrainController::MinsToAnsiTime
AnsiString MinsToAnsiTime(int Input)
converts an integer minute value to string "HH:MM" added at v2.15.0
Definition: TrainUnit.cpp:16524
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:391
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:39
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:216
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:841
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:13021
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:100
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:769
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:812
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:173
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:200
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:862
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:833
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:810
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:522
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:487
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10753
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:984
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:296
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:43
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:379
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:781
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:105
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:239
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:16112
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:294
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7492
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:453
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:707
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1660
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:141
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:361
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:771
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:978
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1021
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:767
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3267
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
Definition: TrainUnit.h:121
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:143
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:413
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:105
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1013
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:121
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6980
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4974
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
< service description
Definition: TrainUnit.h:740
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:405
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:858
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:812
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:976
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6516
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:728
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:52
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:770
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:21525
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
THVShortPair
std::pair< int, int > THVShortPair
Definition: InterfaceUnit.h:82
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:736
TTrainController::TContinuationTrainExpectationEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:738
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:825
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1007
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:876
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:722
TActionVectorEntry::SignallerControl
bool SignallerControl
< string values for timetabled event entries, null on creation
Definition: TrainUnit.h:125
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5254
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:437
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:85
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:726
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:340
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:755
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:732
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:838
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3191
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag = 1, top = 2, top rh diag = 3,...
Definition: TrackUnit.h:90
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:650
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1074
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:998
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:20644
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6900
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:155
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3687
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:127
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2779
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:303
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:206
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7252
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:343
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:41
NotSet
@ NotSet
Definition: TrackUnit.h:76
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:966
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:15037
FinishSequence
@ FinishSequence
Definition: TrainUnit.h:77
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:677
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:75
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:812
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:161
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:67
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:488
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:1000
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:749
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:887
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:247
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:6057
TAllRoutes::TLockedRouteClass::RearTrackVectorPosition
unsigned int RearTrackVectorPosition
the TrackVector position of the rearmost element selected for truncation (this will be truncated)
Definition: TrackUnit.h:1654
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:20386
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:498
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:907
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:180
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:39
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:895
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:417
Bridge
@ Bridge
Definition: TrackUnit.h:66
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:979
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:279
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:369
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:429
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:528
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:37
Buffers
@ Buffers
Definition: TrackUnit.h:66
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:131
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:297
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:121